Java泛型类参数类型推断

时间:2010-06-16 18:09:47

标签: java generics type-inference

鉴于界面:

public interface BasedOnOther<T, U extends BasedList<T>> {

    public T getOther();

    public void staticStatisfied(final U list);

}

我的用例中BasedOnOther<T, U extends BasedList<T>>看起来非常难看。这是因为T类型参数已在BasedList<T>部分中定义,因此“丑陋”来自T需要输入两次。

问题:是否可以让Java编译器在泛型类/接口定义中从T推断出通用BasedList<T>类型?

最终,我想使用如下界面:

class X implements BasedOnOther<Y> {
    public SomeType getOther() { ... }
    public void staticStatisfied(final Y list) { ... }
} // Does not compile, due to invalid parameter count.

Y extends BasedList<SomeType>

相反:

class X implements BasedOnOther<SomeType, Y> {
    public SomeType getOther() { ... }
    public void staticStatisfied(final Y list) { ... }
}

Y extends BasedList<SomeType>

更新:ColinD建议

public interface BasedOnOther<T> {
    public T getOther();
    public void staticSatisfied(BasedList<T> list);
}

无法创建如下的实现:

public class X implements BasedOnOther<SomeType> {
    public SomeType getOther() { ... }
    public void staticStatisfied(MemoryModel list);
} // Does not compile, as it does not implement the interface.

需要MemoryModel extends BasedList<SomeType>的地方(因为它提供了其他方法)。

4 个答案:

答案 0 :(得分:3)

如果您实际上不需要在需要U extends BasedList<T>的某个特定子类/实现的类中执行任何操作,则看起来您实际上并不需要类型参数BasedList<T>。界面可能只是:

public interface BasedOnOther<T> {
  public T getOther();
  public void staticSatisfied(BasedList<T> list);
}

修改:根据您的更新,我认为您无法做到这一点。我想你要么只需要使用原始声明,要么制作一些指定T的中间类型,例如:

public interface BasedOnSomeType<U extends BasedList<SomeType>>
         extends BasedOnOther<SomeType, U>
{
}

public class X implements BasedOnSomeType<MemoryModel> { ... }

这看起来有点浪费,我并不认为原始声明看起来很糟糕。

答案 1 :(得分:2)

这个怎么样?

public interface BasedOnOther<T> {
    public T getOther();
    public <U extends BasedList<T>> void staticStatisfied(final U list);
}

答案 2 :(得分:0)

ColinD几乎是正确的。您可能想要的是:

public interface BasedOnOther<T> {
  public T getOther();
  public void staticSatisfied(BasedList<? extends T> list);
}

那是因为方法参数是协变,但泛型是不变的。看看这个例子:

public test() {
  Number n1;
  Integer n2; //Integer extends Number.  Integer is a more-specific type of Number.

  n1 = n2; //no problem
  n2 = n1; //Type mismatch because the cast might fail.

  List<Number> l1;
  List<Integer> l2;
  List<? extends Number> l3;

  l1 = l3; //No problem.
  l1 = l2; //Type mismatch because the cast might fail.
}

为什么:

尝试将Integer放在Number所属的位置是协方差,并且它通常对于函数参数是正确的。

尝试将Number放在Integer所属的位置是相反的,逆变,并且它通常对函数返回值是正确的。例如,如果您定义了一个返回Number的函数,它可能会返回一个Integer。但是,如果您将其定义为返回一个Integer,则无法返回Number,因为这可能是一个浮点数。例如。

当你处理泛型时,编译器无法判断泛型参数(在你的情况下,T)是否是协变的或逆变的。例如,在你的代码中,T曾经是一个返回值,曾经是参数的一部分。因此,默认情况下,通用参数是不变的。

如果您想要协方差,请使用<? extends T>。对于逆变,请使用<? super T>。根据经验,您可能总是希望在所有公共函数上指定协方差/逆变。对于私有函数而言,它并不重要,因为您通常已经知道了类型。

这不是特定于java,其他面向对象的语言也有类似的问题。

答案 3 :(得分:0)

我最近遇到了一个非常类似的问题。

我建议,如果你不需要专门提及MemoryModel,即如果U extends BasedList<T>足够,那么我肯定会做Pepe回答的事情。

但是,如果你必须为两个方法必须特别使用MemoryModel的两种方法键入check,并且Pepe的答案中的类型推理是不够的,那么使用笨拙/冗长的唯一方法参数化构造函数更简单,就是利用泛型方法参数推理。您需要为每个构造函数创建通用的静态工厂方法,其中工厂方法执行类型推理(构造函数不能在Java中键入推理)。

中介绍了如何执行此操作
  

有效的Java,Joshua Block;第27项:偏好通用方法

我也解释了这个并引用了解决方案(带代码)here