Java泛型-为什么允许“扩展T”但不允许“实现T”?


306

我想知道Java中是否有特殊原因总是使用“ extends”而不是“ implements”来定义类型参数的界限。

例:

public interface C {}
public class A<B implements C>{} 

被禁止,但

public class A<B extends C>{} 

是正确的。是什么原因呢?


14
我不知道为什么人们认为Tetsujin no Oni的答复确实回答了这个问题。它基本上使用学术措辞重新表达了OP的观察,但没有给出任何推理。“为什么没有implements?” -“因为只有extends”。
ThomasR

1
ThomasR:这是因为这不是“允许”的问题,而是含义:无论如何编写使用约束的类型的泛型,无论约束是来自接口还是祖先类型,都没有区别。
Tetsujin no Oni

添加了一个答案(stackoverflow.com/a/56304595/4922375),并附上我的推理,为什么implements不带来新的东西并使事情变得更加复杂。希望对您有帮助。
安德鲁·托比尔科

Answers:


328

在类“实现”还是“扩展”之间,通用约束语言没有语义差异。约束可能性是“扩展”和“超”-也就是说,该类是可分配给其他类的对象(扩展),还是该类可从该类分配(超级)。


@KomodoDave(我认为答案旁边的数字将其标记为正确,我不确定是否还有其他方法可以将其标记,有时其他答案可能包含其他信息-例如,我遇到了无法解决的特定问题, google在搜索时将您发送到这里。)@Tetsujin no Oni(是否可以使用一些代码进行澄清?thanx :))
ntg 2014年

@ntg,这是一个很好的例子,可以帮助您寻找问题的例子-我将其链接在评论中,而不是将其嵌入答案中。stackoverflow.com/a/6828257/93922
Tetsujin no Oni 2014年

1
我认为原因至少是我希望拥有一个泛型,该泛型可以具有一个构造函数和方法,这些构造函数和方法可以接受既可以扩展基类又可以显示接口的任何类,而不仅仅是扩展接口的接口。然后对接口的存在进行通用测试的实例化,并将实际的类指定为类型参数。理想情况下,我希望 class Generic<RenderableT extends Renderable implements Draggable, Droppable, ...> { Generic(RenderableT toDrag) { x = (Draggable)toDrag; } }有人要进行编译时检查。
彼得

1
@peterk有了RenderableT,您就可以扩展Renderable,Draggable,Droppable...。
Tetsujin no Oni

@TetsujinnoOni,我不需要的是仅在接受实现某些接口集合的类的编译时强制实施,然后能够在泛型中引用OBJECT类(显示那些接口),然后知道至少在编译时(最好在运行时),可以将分配给泛型的任何内容安全地强制转换为任何指定的接口。现在不是实现Java的情况。但这会很好:)
彼得

77

答案在这里  :

要声明有界的类型参数,请列出类型参数的名称,后跟extends关键字,然后是其上限 […]。注意,在这种情况下,extends在一般意义上用于表示extends(如在类中)或implements(如在接口中)。

因此,有了它,这有点令人困惑,Oracle知道了。


1
更令人困惑的是,getFoo(List<? super Foo> fooList) 只能使用Foo like扩展了字面的类class Foo extends WildcardClass。在这种情况下,a List<WildcardClass>是可接受的输入。但是,任何Foo实现的类均无法正常工作class Foo implements NonWorkingWildcardClass并不意味着List<NonWorkingWildcardClass>会在中有效getFoo(List<? super Foo> fooList)。晶莹剔透!

19

可能是因为对于双方(B和C),仅类型是相关的,而不是实现。在你的例子中

public class A<B extends C>{}

B也可以是接口。“扩展”用于定义子接口以及子类。

interface IntfSub extends IntfSuper {}
class ClzSub extends ClzSuper {}

我通常认为“ Sub扩展Super”是“ Sub就像Super一样,但是具有附加功能”,而“ Clz实现Intf”是“ ClzIntf的实现”。在您的示例中,这将匹配:B类似于C,但是具有附加功能。功能在这里是相关的,而不是实现。


10
考虑<B扩展D&E>。E <caps>一定不是</ caps>是一个类。
Tom Hawtin-大头钉

7

基本类型可能是通用参数,因此实际类型可能是类的接口。考虑:

class MyGen<T, U extends T> {

同样从客户端代码的角度来看,接口几乎与类没有区别,而对于子类型而言,它很重要。


7

这是一个允许在何处进行扩展以及可能需要的扩展性的示例:

public class A<T1 extends Comparable<T1>>


5

使用哪种术语是任意的。可能是任何一种方式。也许语言设计人员认为“扩展”是最基本的术语,而“实现”则是接口的特殊情况。

但是我认为implements这会更有意义。我认为传达更多信息是,参数类型不必处于继承关系中,它们可以处于任何子类型关系中。

Java术语表表达了类似的观点


3

我们习惯了

class ClassTypeA implements InterfaceTypeA {}
class ClassTypeB extends ClassTypeA {}

与这些规则的任何细微差异都会使我们感到困惑。

类型绑定的语法定义为

TypeBound:
    extends TypeVariable 
    extends ClassOrInterfaceType {AdditionalBound}

JLS 12> 4.4。类型变量>TypeBound

如果我们要更改它,我们肯定会添加implements大小写

TypeBound:
    extends TypeVariable 
    extends ClassType {AdditionalBound}
    implements InterfaceType {AdditionalBound}

并以两个相同处理的子句结束

ClassOrInterfaceType:
    ClassType 
    InterfaceType

JLS 12> 4.3。引用类型和值>ClassOrInterfaceType

除了我们还需要注意implements,这会使事情进一步复杂化。

我相信这是为什么主要原因extends ClassOrInterfaceType是代替extends ClassTypeimplements InterfaceType-让事情变得简单了复杂的概念之内。问题在于我们没有一个合适的词来覆盖这两个词extendsimplements而且我们绝对不想引入一个词。

<T is ClassTypeA>
<T is InterfaceTypeA>

尽管extends在与接口一起使用时会带来一些混乱,但是它是一个广义的术语,可以用来描述这两种情况。尝试将您的思想调整为扩展类型的概念(不扩展类,不实现接口)。您可以通过另一种类型来限制类型参数,而该类型实际上是什么并不重要。唯一重要的是它是它的上限和它的超类型


-1

实际上,当在接口上使用通用时,关键字也为extends。这是代码示例:

有2个类实现了Greeting接口:

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();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.