我对泛型有疑问。所以我要说我有3个班级" Foo"," Bar" (这是Foo的一个子类),一个类"工厂"和界面" TestInterface"。 我想写一个通用方法,只允许直接"直接"实现接口,所以没有子类。 这对我来说非常困难,因为Java8似乎更聪明了#34;比Java7。
这是一个例子:
public interface TestInterface<T extends TestInterface<T>> {
}
public class Foo implements TestInterface<Foo>{
}
public class Bar extends Foo {
}
public class Factory {
public static <T extends TestInterface<T>> T doSth(T arg) {
return arg;
}
}
好的,这些是我的课程。现在在doSth(T arg)
方法中我想在这种情况下允许Foo对象,因为它是唯一直接&#34;直接&#34;实现接口。
显然,这样就不会立即编译:
Bar b = Factory.doSth(new Bar());
因为Bar没有实现TestInterface<Bar>
。
好的我现在的问题是,这行将编译没有任何问题:
Foo f = Factory.doSth(new Bar());
即使通用参数是T和T应该用它自己的类实现接口(对不起,真的不知道怎么说,希望你知道我的意思),编译器接受&#34; Bar&#34;作为参数。
所以编译器必须表现得像&#34;好吧Bar不适合,但也许超类适合。&#34;然后它就会看到&#34;它适用于superlcass&#34; Foo&#34;。 然后酒吧将被视为一个Foo,因为每个酒吧都是某种Foo或某样的?至少这是我的想象。
好吧,如果我尝试使用Java7编译该行,它就会失败。所以看起来Java8在类型擦除方面有点聪明了吗?!
Java8中是否有任何方法不允许子类,这样就不会编译?:
Foo f = Factory.doSth(new Bar());
希望你理解我的问题,我很少用英文写作。
迎接
答案 0 :(得分:3)
Foo f = Factory.doSth(new Bar());
在Java 8而不是Java 7中工作的原因是因为Java 8具有更好的类型推断。在Java 7中,类型被推断为:
Foo f = Factory.<Bar>doSth(new Bar());
Bar
未实现TestInterface<Bar>
,但它通过继承实现TestInterface<Foo>
。 Java 8的类型推断得到了提升,类型将被推断为:
Foo f = Factory.<Foo>doSth(new Bar());
请注意,Factory.<Foo>doSth()
需要Foo
作为参数,因此new Bar()
会隐式强制转换为Foo
,而且工作正常。
我担心没有办法解决这个问题。您将始终能够将对象转发到其超类。您可以Foo
final
,但不允许完全Foo
的子类化。
答案 1 :(得分:1)
我同意这种情况确实存在; Bar
在概念上是Foo
的子类,但我们不希望让它们共享同一个方法doSth()
。
它不完全相同,但在类似情况下,在许多情况下应避免在超子类之间共享equals()
方法,以保持等价关系的对称性和传递性。 (详见J.B.的Effective Java 2nd ed。第8项)
到目前为止,我找不到一个通用参数描述的好解决方案。 (Java的类型系统,就像其他典型的OO语言一样),似乎基本上假设每对超类和子类都应默认共享超类的方法。)相反,我发现在这种情况下丢弃继承将是一种可能的解决方案。 ,如下,
public class Bar { // using composition, instead of inheritance
Foo Bar(Foo f) {
this.f = f;
}
// here, definitions of delegated methods for each of f's methods
// see details in Item 16 of Effective Java 2nd ed. by J.B.
Foo asFoo(){
return f;
}
}
通过Bar
的这种修改,我们可以在这里得到一个编译时错误,
Foo b = Factory.doSth(new Bar(new Foo())); // a compile time error occurs
即使我们在JDK7中使用<Foo>
。
Foo b = Factory.<Foo>doSth(new Bar(new Foo())); // a compile time error occurs
另一方面,如有必要,我们可以使用asFoo()
来避免此错误。
Foo b = Factory.<Foo>doSth(new Bar(new Foo()).asFoo()); // no compile time error