Java中的嵌套类型参数

11

这只是我编写的一个示例,旨在简化我的真实代码,所以如果有些牵强,我深表歉意。我想要做的是从单个嵌套类型参数中有效地获取两个类型参数。我很确定这是不可能的,但我想试一试。

//Not legal java code
public class Foo<C extends Collection<T>> { //where T is another type parameter
    private C coll;

    public Foo(C coll) {
        this.coll = coll;
    }

    public void add(T elem){
        this.coll.add(elem);
    }
    //UPDATED TO ADD GETTER
    /**
     * I may need to retrieve the collection again, or pass it
     * on to another function that needs the specific C type
     */
    public C getColl(){
        return coll;
    }
}
...
List<String> strings = new ArrayList<String>();
Foo<List<String>> foo = new Foo<List<String>>(strings);
foo.add("hello");

我知道可以通过添加另一个类型参数来实现:

public class Foo<C extends Collection<T>,T>

但是我不得不添加冗余内容:

Foo<List<String>,String> foo = new Foo<List<String>,String>(strings);

在我的实际案例中,我的泛型有时可以在implements子句中指定,例如:

public class Bar implements Baz<String>

不得不指定第二个类型参数,这更加令人痛苦,因为它感觉像是把实现细节摆在我的面前。必须这样说:

Foo<Bar,String>

当String和Bar之间已经有了关系时,似乎显得不太优雅。我知道这是Java的特点,但我只是好奇是否有解决方案。

3个回答

6

这是不可能的,而且我认为也不理想,因为你现有的类中没有任何需要不变性。

Foo<T,C extends Collection<T>>

可以更普遍地说

Foo<T,C extends Collection<? super T>>

如果唯一的原因是允许对集合进行突变,那么就需要使用T。

请注意,如果您担心频繁指定两个类型参数,可以创建一个浅层子类:

class DerivedFoo<T> extends Foo<Collection<T>,T>

您可以使用工厂方法来避免在创建时重复指定

public static <T> Foo<Collection<T>,T> fromCollection(Collection<T> c)

你也可以将接口抽象为 interface,以获得与上面的 DerivedFoo 相同的简洁类型的好处。

工厂方法的思想很有趣,但是让我困扰的是即使我的代码规定它们始终相同,类型仍然被指定了两次。 - Russell Leggett
@RusselLeggett,好的,所以您确实需要不变性,没有Collection<? super T>吗?是的。那很烦人。我的建议是在您的库中处理复杂性,并通过具有返回类型interface Foo<T> extends ComplicatedFoo<CollectionSubType<T>,T>的工厂来尝试公开简洁的API,以便客户端只需使用单个参数版本。 - Mike Samuel

3

为什么不只使用T作为您唯一的类型参数,例如:

public class Foo<T> { //where T is another type parameter
private Collection<T> coll;

public Foo(Collection<T> coll) {
    this.coll = coll;
}

public void add(T elem){
    this.coll.add(elem);
}

我很好奇为什么这也不是解决方案。 - ty1824
3
正如我之前所说,这只是一个人为制造的例子。但如果放入的集合类型也很重要呢?它可以是列表、集合或TreeList。假设我添加了一个getter来再次检索集合,那么类型就很重要了。 - Russell Leggett

2

在Java7之前,构造函数不具有类型推断功能,解决方法是使用静态工厂方法。现在已经不再需要了。在Java 7中,你可以

Foo<List<String>,String> foo = new Foo<>(strings);

关于 TC,如果我们有两个带有约束关系的类型参数,那么一定会存在某种程度的冗余。在您的示例中,由于一个参数 C 完全决定了另一个参数 T,这种冗余似乎难以承受。我看不到解决方案。
但是如果重新排列类型参数,您可能会感觉更好。
Foo<String,Bar> foo = new Foo<>(bar);

首先我们声明一个String;然后进一步提供一个Baz<String>,它是Bar


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接