Java中的泛型让我感到困惑

6
有人能解释一下为什么以下代码段可以编译通过吗?
List<Long> longNums = new ArrayList(Arrays.asList("one", "two", "three"));

这是由于类型擦除导致的吗?

如果我有以下方法:

public <T> T readProperty(String propName, Class<T> type);

如何确保它返回的是List<Long>而不是List<String>?显然,在调用方法时我只能提供List.class并祈祷。

//run and pray
List<Long> longNums = readProperty("prop", List.class);

我曾经遇到过这样的情况,一个方法错误地将字符串对象列表分配给长整型数字的列表,直到我运行它并看到ClassCastException时才发现。


6
简而言之,不要将泛型和裸类型混合使用。 - Konstantin Yovkov
你为什么想要将字符串字面量存储在“Long”列表中? - Abimaran Kugathasan
不是字符串文字,而是字符串列表。也许我应该重新表述。 - Preslav Rachev
5个回答

9

由于您没有正确使用范型,因此不应期望程序能正确运行。

首先,您不应将范型与原始类型混合使用,这意味着以下语句:

List<Long> longNums = new ArrayList(Arrays.asList("one", "two", "three"));

无效(从正确性角度来看)。如果它是:

List<Long> longNums = new ArrayList<Long>(Arrays.asList("one", "two", "three"));

如果代码无法编译,你会在编译时就收到错误提示(而不是运行时,这可能更加令人困惑和害怕)。

因此,为了能够编译通过,你的列表应该被定义为:

List<String> longNums = new ArrayList<>(Arrays.asList("one", "two", "three"));

此外,关于这个声明。
//run and pray
List<Long> longNums = readProperty("prop", List.class);

实际上没有办法确保readProperty会返回List<Long>,所以你必须要么进行强制类型转换,要么添加一个@SuppressWarnings注释。这是由于编译器的类型擦除特性引起的 - 参数化类型Long在运行时被擦除并且在实际执行readProperty()并通过反射获取prob值时已经消失。


是的,我知道。第一部分更多是第二部分的示例说明。有什么解决方法? - Preslav Rachev
谢谢。我希望有一种方法可以在参数中指定我真的、真的期望一个List<Long>。类似于(如果存在)List<Long>.class。我的大问题是,赋值在运行时没有问题,只有当我尝试访问列表元素时才发现问题。我尝试了显式转换为(List<Long>),但似乎也没有帮助。 - Preslav Rachev
如果你敢打赌在运行时列表只包含“Long”对象(这样就不会有ClassCastException),那么你可以忽略编译器的警告。 :) - Konstantin Yovkov
在这里完全没有编译器警告:List<Long> longNums = readProperty("prop", List.class); 它完全干净。 - Preslav Rachev
只是一个小细节,在 List<Long> longNums 中的 long 不是参数类型,而是一个 参数化类型 - William Kinaan

1

你错过了钻石符号 <>(这将无法编译):

List<Long> longNums = new ArrayList<>(Arrays.asList("one", "two","three"));

1
这个甚至都无法编译 :) - Konstantin Yovkov
@kocko 这是真的。但至少这将防止运行时错误 :) - Maroun

1
这实际上是两个不同的问题。
List<Long> longNums = new ArrayList(Arrays.asList("one", "two", "three"));

由于源代码兼容性的原因,在这些情况下可以使用原始类型。但不应该依赖此功能,因为这样会破坏泛型的整个设计理念。

至于第二个问题,你可以通过以下方式规避问题:

public <T> List<T> readListProperty(String propName, Class<T> type);
...
List<Long> longNums = readProperty("prop", Long.class);

这更像是一种变通方法而不是解决方案,并且并非总是适用。 (例如,当您需要更复杂的返回类型时,如Map<String,List<String>>)。

很遗憾,我无法触及方法签名。此外,我尝试将结果显式转换为List<Long>,但这也没有帮助。赋值仍然顺利进行。只有当我尝试访问列表元素时,才会发现转换问题。 - Preslav Rachev
如果你不能改变方法,这就是不幸的现实,没有编译时的技巧可以帮助你避开这个问题。你可以在运行时手动检查结果,但仅检查单个元素是不够的,因为你的列表可能包含6个“Long”后面跟着一个“String”。 - biziclop

0
如果您将一个列表定义为ArrayList,Java 就不知道其中包含什么类型的对象。您可以将其分配给ArrayList<Long>,但是当您调用项目上的方法或属性时,会出现错误,因为类型不匹配。

0

通用类:

public abstract class GenericClass<T> {

private T managedClass;
public List<T> readListProp(String propName,T object){
    //do stuff using T type instead of Long,String,Double,etc
}
Other classes:
public class FirstClass extends GenericClass<String>
{...
//you have a class where T from generic is replaced with String
} 
public class SecClass extends GenericClass<Double>
{...
//you have a class where T from generic is replaced with Double
}

这个答案看起来似乎与问题无关。如果有关系,请解释一下! - anderas

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