扩展通用类

67
public class MyGeneric<T, E> {}

public class Extend1<T, E> extends MyGeneric<T, E> {}

public class Extend2 extends MyGeneric<String, Object> {}

据我所知,上面例子中的两个子类都是有效的。我想知道Java如何知道在实例化子类时给定的超类中的类型何时会被定义,以及何时它们是实际的类名(即它如何知道T、E不是类名)?
另外,是否可以使用多个字母表示泛型类型(即使很少见)?如果(通过某些严重的规划错误)这些类型与现有类冲突怎么办?
public class E{}
public class Foo<E>{}

那么接下来会发生什么?

编辑:感谢您的迅速回答。为了回答我的第一个问题,Joachim的答案是最有效的。(原文链接)

为了回答这个附带问题,aioobe的答案更清晰。(原文链接)

3个回答

75

让我们来看一下这个定义:

public class Extend1<T, E> extends MyGeneric<T, E> {}

这里的TE各自出现了两次并扮演了两种不同的角色。

  • Extend1<T,E>中,你需要定义类型参数。这意味着类型Extend1有两个(无界)类型参数TE告诉Java编译器那些使用Extend1的人需要指定类型。
  • extends MyGeneric<T,E>中,你使用先前定义的类型参数。如果这里不知道TE是类型参数,那么TE将成为简单的类型引用,即编译器将寻找名为TE的类(或接口...)(并且很可能找不到它们)。

是的,类型参数遵循Java中任何其他标识符的语法规则,因此你可以使用多个字母ABC甚至可能会混淆的名称(使用名为String的类型参数是合法的,但极度令人困惑)。

单个字母的类型参数名称只是一种非常常见的命名策略。


3
当使用"Extend1<String> extends Generic<String>"时,编译器会将第一个"String"视为类型参数(与java.lang.String不同),而将第二个"String"视为对第一个"String"的类型引用。 - sma
1
@pegausbupt:确切地说,这将创建一个类型参数名为“String”,它与java.lang.String没有实际关系,除了让人们感到困惑并隐藏类型的简称。 - Joachim Sauer
Extend1<T, E> 中,TE类型参数。当发生泛型类型调用时,extends MyGeneric<T, E> 中的 TE 将被替换为类型参数,以替换 Extend1<T, E> 中的类型参数 TE。因此,实际上,泛型和非泛型类型都可以仅扩展/实现非泛型类型。 - rosshjb

26
我想知道当子类实例化时,Java如何知道超类中给定的类型何时被定义,以及何时它们是实际的类名(即,它如何知道T、E不是类名)?
Java并不关心这个问题。如果你关心...
class MyGeneric<String> extends ArrayList<String> {
    String value;
}

可以使用任何有效的Java标识符作为类型参数,即使不常见也可以。名称可能会冲突,但Java不会将其视为错误。在<...>之间的标识符始终被视为类型参数,无论该标识符是否对应于类名。这可能会变得非常混乱,以下是一个示例:
class MyGeneric<String> extends java.util.ArrayList<String> {
    String value;
}

class Test {
    public static void main(String... args) throws Exception {
        MyGeneric<Integer> obj = new MyGeneric<Integer>();
        obj.value = 5;
        //          ^
        //          |
        //          '--- Assign an integer to what seems to be a String!
    }
}

类似问题:


1
谢谢,这解释得很清楚(虽然我不得不读几遍才能理解) - James
没问题,不用谢 :-) 顺便问一下,好问题。我看到其他问题问为什么 class MyList<Integer> extends ArrayList<Integer> 不像预期的那样工作 :-) - aioobe
1
澄清一下 - 如果你在那里放置一个类型名称,比如String,那么当引用MyGeneric对象时,String不再表示java.lang.String,它现在表示“我命名的某个通用类型”。 - luketorjussen

3

以下内容没有问题:

public class E{}
public class Foo<E>{}

因为在 Foo<E> 的上下文中,E 是一个类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接