没有。具体来说,所有列表都只是列表,在运行时,泛型已消失。如果编译器/编辑器无法告诉您,那么您也无法在运行时告诉您。
为了更具体一点,这两者之间存在零区别:
void example() {
List<String> list1 = new ArrayList<String>();
List<List<String>> list2 = new ArrayList<List<String>>();
}
100% 相同 - 您无法以任何方式或形式对这些列表进行任何操作来区分它们。
你可以绕过它——例如,给定一个List<?>,如果你在这个上面运行.get(0),你得到的是一个List(同样,不可能说它是一个List<String>或List<WhoKnowsWhat>,只是它是一些列表),那么显然它不是字符串列表。可以是对象列表,也可以是字符串到整数的映射列表列表。没办法知道。
如果列表为空,则此 hack 不起作用。这不是一个好主意。
那么解决方案是什么?
修复您的代码。在第一种情况下,您不应该处于这种情况。在您的代码库中的某处,您创建一个字符串列表或一个字符串列表列表,然后将其返回。不要这样做,而是想出另一种方法。例如,使用类型层次结构:
public interface FullName {
public List<String> allNames();
public String rendered();
public List<String> get(int idx);
}
然后为各种“形式”编写实现。这是一个输入为List<String> 的实现:
private class SimpleName implements FullName {
private final List<String> name;
public SimpleName(List<String> name) {
this.name = name;
}
@Override public List<String> allNames() {
return name;
}
@Override public String rendered() {
return name.stream().collect(Collectors.joining(" "));
}
@Override public List<String> get(int idx) {
if (idx == 0) return name;
throw new IndexOutOfBoundsException();
}
}
主要是与这些交互的代码不需要关心你有什么样的名字;您可以想象对“名称”所做的任何操作都应封装在此接口中,并且不同的实现可以包含有关如何执行这些任务的所有相关知识(以代码表示)。在调用代码确实需要知道的特殊情况下,它们可以——要么公开方法,要么只进行实例检查。