鉴于界面:
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>
的地方(因为它提供了其他方法)。
答案 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。