在Java中,为什么在定义类型参数边界时总是使用“extends
”而不是“implements
”?是否有特殊原因?
例如:
public interface C {}
public class A<B implements C>{}
被禁止,但是
public class A<B extends C>{}
这是正确的。那是什么原因?
在泛型约束语言中,“实现(implements)”和“扩展(extends)”之间没有语义差异。约束的可能性是“扩展(extends)”和“超级(super)” - 即,这个类是否能够分配到另一个类(extends),或者这个类是否可以从那个类分配(super)。
class Generic<RenderableT extends Renderable implements Draggable, Droppable, ...> { Generic(RenderableT toDrag) { x = (Draggable)toDrag; } }
需要在编译时进行检查。 - peterk所以,以上就是答案,虽然有点混乱,但 Oracle 心知肚明。为了声明一个有界类型参数,需要列出该类型参数的名称,然后是
extends
关键字,接着是它的上限[...]。请注意,在这个语境中,extends
一般用于表示extends
(如类)或implements
(如接口)。
getFoo(List<? super Foo> fooList)
只适用于直接由 Foo
扩展的类,例如 class Foo extends WildcardClass
。在这种情况下,List<WildcardClass>
将是可接受的输入。但是,任何 Foo
实现的类都不行,例如 class Foo implements NonWorkingWildcardClass
并不意味着 List<NonWorkingWildcardClass>
在 getFoo(List<? super Foo> fooList)
中是有效的。完全清楚! - Ray可能是因为对于B和C双方而言,只有类型是相关的,而不是实现方式。在您的示例中
public class A<B extends C>{}
B也可以是一个接口。 "extends"用于定义子接口以及子类。
interface IntfSub extends IntfSuper {}
class ClzSub extends ClzSuper {}
我通常认为'Sub extends Super'是指'Sub类似于Super,但具有额外的功能',而 'Clz implements Intf'是指'Clz实现了Intf接口'。在您的示例中,这将匹配: B类似于C,但具有额外的功能。这里相关的是功能,而不是实现。
E
应该代表一个实际的类型名称(我想 - 我在十年前写了那条评论)。 - Tom Hawtin - tackline可能基础类型是一个泛型参数,因此实际类型可能是接口或类。例如:
class MyGen<T, U extends T> {
从客户端代码的角度来看,接口和类几乎无法区分,但对于子类型而言,这一点非常重要。
我们习惯于
class ClassTypeA implements InterfaceTypeA {}
class ClassTypeB extends ClassTypeA {}
任何与这些规则稍有偏差都会使我们混淆不清。
类型边界的语法定义如下:
TypeBound:
extends TypeVariable
extends ClassOrInterfaceType {AdditionalBound}
(JLS 12 > 4.4. Type Variables > TypeBound
)
如果我们对它进行更改,肯定会添加 implements
案例。
TypeBound:
extends TypeVariable
extends ClassType {AdditionalBound}
implements InterfaceType {AdditionalBound}
最终会得到两个完全相同的处理子句
ClassOrInterfaceType:
ClassType
InterfaceType
(JLS 12 > 4.3. 引用类型和值 > ClassOrInterfaceType
)
不过我们还需要考虑 implements
,这会让事情更加复杂。我相信这就是为什么使用 extends ClassOrInterfaceType
而不是 extends ClassType
和 implements InterfaceType
的主要原因 - 为了在复杂的概念中保持简单性。问题是我们没有合适的词来涵盖 extends
和 implements
,我们绝对不想引入一个新词。
<T is ClassTypeA>
<T is InterfaceTypeA>
尽管 extends
在与接口一起使用时会带来一些混乱,但它是一个更广泛的术语,可以用于描述两种情况。试着将思维调整到扩展类型的概念上(而不是 扩展类或 实现接口)。您通过另一个类型限制类型参数,无论该类型实际上是什么都无所谓。重要的是它是其上限和其超类型。
使用哪种术语有些随意,可以任选其一。也许语言设计师认为“extends”是最基本的术语,“implements”是接口的特殊情况。
但我认为,“implements”稍微更合理一些。我认为这样更能传达参数类型不必在继承关系中,而可以处于任何子类型关系中。
Java词汇表表达了类似观点。
interface Greeting {
void sayHello();
}
class Dog implements Greeting {
@Override
public void sayHello() {
System.out.println("Greeting from Dog: Hello ");
}
}
class Cat implements Greeting {
@Override
public void sayHello() {
System.out.println("Greeting from Cat: Hello ");
}
}
测试代码如下:
@Test
public void testGeneric() {
Collection<? extends Greeting> animals;
List<Dog> dogs = Arrays.asList(new Dog(), new Dog(), new Dog());
List<Cat> cats = Arrays.asList(new Cat(), new Cat(), new Cat());
animals = dogs;
for(Greeting g: animals) g.sayHello();
animals = cats;
for(Greeting g: animals) g.sayHello();
}
implements
不会带来任何新东西,反而会进一步复杂化事情。我希望这对你有所帮助。 - Andrew Tobilko