Java 泛型 - 不允许子类
Java generics - Don't allow subclasses
我有一个关于泛型的问题。假设我有 3 个 classes "Foo"、"Bar"(它是 Foo 的子 class)、一个 class "Factory" 和一个界面 "TestInterface"。
我想写一个通用方法,只允许 "directly" 实现接口的对象,所以没有 subclasses。
这对我来说很难,因为 Java8 似乎比 Java7.
"smarter"
下面是一个例子:
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;
}
}
好的,这些是我的 classes。现在在 doSth(T arg)
方法中,我想在这种情况下允许 Foo 对象,因为它是 "directly" 实现接口的唯一 class。
显然这样的东西现在无法编译:
Bar b = Factory.doSth(new Bar());
因为 Bar 没有实现 TestInterface<Bar>
。
好的,我现在的问题是,这一行将毫无问题地编译:
Foo f = Factory.doSth(new Bar());
即使通用参数是 T 并且 T 应该用自己的 class 实现接口(抱歉,真的不知道怎么说,希望你明白我的意思),编译器接受 "Bar"作为参数。
所以编译器的行为必须像 "okay Bar doesn't fit, but maybe a superclass would fit." 然后它 "sees" 它与 superlcass "Foo" 一起工作。
然后 Bar 将被视为 Foo,因为每个 Bar 都是某种 Foo 或类似的东西?至少我是这么想的。
好吧,如果我尝试用 Java7 编译该行,它会失败。所以看起来 Java8 使用类型擦除更聪明一些?!
在 Java8 中是否有任何方法不允许子classes,这样就不会编译?:
Foo f = Factory.doSth(new Bar());
希望你能理解我的问题,我很少用英文写作。
问候
我同意这种情况确实存在; Bar
是 'conceptually' Foo
的子 class,但我们不想让它们共享相同的方法 doSth()
。
它并不完全相同,但类似地,在许多情况下应避免在超子 class 之间共享一个 equals()
方法,以保持等价关系的对称性和传递性。 (详情见 Java 第二版 J.B 的第 8 项。)
到目前为止,我未能找到具有泛型参数描述的好的解决方案。 (Java 的类型系统,像其他典型的 OO 语言一样,似乎基本上假设每对超级和子 classes 应该默认共享超级 class 的方法。 ) 相反,我发现在这种情况下丢弃继承将是一个可能的解决方案,如下,
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
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
,效果很好。
恐怕没有办法解决这个问题。您将始终能够将一个对象向上转换为它的超对象 class。您可以制作 Foo
final
,但这完全不允许 Foo
的子 classing。
我有一个关于泛型的问题。假设我有 3 个 classes "Foo"、"Bar"(它是 Foo 的子 class)、一个 class "Factory" 和一个界面 "TestInterface"。 我想写一个通用方法,只允许 "directly" 实现接口的对象,所以没有 subclasses。 这对我来说很难,因为 Java8 似乎比 Java7.
"smarter"下面是一个例子:
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;
}
}
好的,这些是我的 classes。现在在 doSth(T arg)
方法中,我想在这种情况下允许 Foo 对象,因为它是 "directly" 实现接口的唯一 class。
显然这样的东西现在无法编译:
Bar b = Factory.doSth(new Bar());
因为 Bar 没有实现 TestInterface<Bar>
。
好的,我现在的问题是,这一行将毫无问题地编译:
Foo f = Factory.doSth(new Bar());
即使通用参数是 T 并且 T 应该用自己的 class 实现接口(抱歉,真的不知道怎么说,希望你明白我的意思),编译器接受 "Bar"作为参数。
所以编译器的行为必须像 "okay Bar doesn't fit, but maybe a superclass would fit." 然后它 "sees" 它与 superlcass "Foo" 一起工作。 然后 Bar 将被视为 Foo,因为每个 Bar 都是某种 Foo 或类似的东西?至少我是这么想的。
好吧,如果我尝试用 Java7 编译该行,它会失败。所以看起来 Java8 使用类型擦除更聪明一些?!
在 Java8 中是否有任何方法不允许子classes,这样就不会编译?:
Foo f = Factory.doSth(new Bar());
希望你能理解我的问题,我很少用英文写作。
问候
我同意这种情况确实存在; Bar
是 'conceptually' Foo
的子 class,但我们不想让它们共享相同的方法 doSth()
。
它并不完全相同,但类似地,在许多情况下应避免在超子 class 之间共享一个 equals()
方法,以保持等价关系的对称性和传递性。 (详情见 Java 第二版 J.B 的第 8 项。)
到目前为止,我未能找到具有泛型参数描述的好的解决方案。 (Java 的类型系统,像其他典型的 OO 语言一样,似乎基本上假设每对超级和子 classes 应该默认共享超级 class 的方法。 ) 相反,我发现在这种情况下丢弃继承将是一个可能的解决方案,如下,
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
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
,效果很好。
恐怕没有办法解决这个问题。您将始终能够将一个对象向上转换为它的超对象 class。您可以制作 Foo
final
,但这完全不允许 Foo
的子 classing。