为什么我不能显式地将类型参数传递给泛型Java方法?

时间:2020-03-05 18:42:34  来源:igfitidea点击:

我定义了一个Java函数:

static <T> List<T> createEmptyList() {
    return new ArrayList<T>();
}

一种调用方式是这样的:

List<Integer> myList = createEmptyList(); // Compiles

为什么不能通过显式传递泛型类型参数来调用它? :

Object myObject = createEmtpyList<Integer>(); // Doesn't compile. Why?

我从编译器收到错误"表达式的非法开头"。

解决方案

回答

自从我深入研究Java的各个部分以来已经有一段时间了,但是...

为什么我们不能这样做,可能是语言开发人员的设计选择。尽管如此,由于Java使用的类型擦除,泛型信息总会在编译时被丢弃,因此在示例中,无论我们是否具有类型参数,它都会创建完全相同的字节码。

回答

如果将类型作为方法参数传递,则可以。

static <T> List<T> createEmptyList( Class<T> type ) {
  return new ArrayList<T>();
}

@Test
public void createStringList() {
  List<String> stringList = createEmptyList( String.class );
}

不能以与类型相同的方式来泛化方法,因此,对于具有动态类型的泛型返回类型-phew的方法,唯一的选择是:-)-将类型作为参数传递。

有关Java泛型的真正优秀的FAQ,请参阅Angelika Langer的泛型FAQ。


跟进:

在这种情况下,像在Collection.toArray(T [])中那样使用数组参数是没有意义的。使用数组的唯一原因是因为使用相同(预分配)的数组来包含结果(如果数组足够大以适合所有结果)。这样可以节省在运行时始终分配新数组的时间。

但是,出于教育目的,如果我们确实想使用数组类型,则语法非常相似:

static <T> List<T> createEmptyList( T[] array ) {
  return new ArrayList<T>();
}

@Test
public void testThing() {
  List<Integer> integerList = createEmptyList( new Integer[ 1 ] );
}

回答

@pauldoo
是的,你说的很对。这是java泛型imho的弱点之一。

我想对Cheekysoft做出回应,我还建议考虑一下Java人员自己是如何完成的,例如T [] AbstractCollection#toArray(T [] a)。我认为Cheekysofts版本更好,但是Java拥有熟悉的优势。

编辑:添加了链接。
重新编辑:在SO上发现一个错误:)

Cheekysoft的后续行动:
好吧,因为它是应该返回的某种类型的列表,所以相应的示例应类似于:

static <T> List<T> createEmptyList( List<T> a ) {
  return new ArrayList<T>();
}

但是可以,通过类对象显然是更好的选择。我唯一的论点是熟悉,在这个确切的例子中,它并不值钱(实际上是不好的)。

回答

当Java编译器无法自行推断静态方法的参数类型时,我们始终可以使用完整的合格方法名称Class来传递它。 <类型> method();

Object list = Collections.<String> emptyList();