Java 需要使用泛型将列表筛选到特定的子类
我有一个Java 需要使用泛型将列表筛选到特定的子类,java,generics,compiler-warnings,Java,Generics,Compiler Warnings,我有一个列表,其中包含一个特定的超类(如Vehicle),我想编写一个方法,返回该列表中的对象,这些对象是特定子类(如Car)的实例 到目前为止,我有这个,但它会生成一个典型的“未检查”操作编译器警告: public <T extends Vehicle> List<T> getVehiclesOfType(Class<T> type) { List<T> result = new ArrayList<T>(); fo
列表
,其中包含一个特定的超类(如Vehicle),我想编写一个方法,返回该列表中的对象,这些对象是特定子类(如Car)的实例
到目前为止,我有这个,但它会生成一个典型的“未检查”操作编译器警告:
public <T extends Vehicle> List<T> getVehiclesOfType(Class<T> type) {
List<T> result = new ArrayList<T>();
for (Vehicle vehicle : getVehicles()) {
if (type.isAssignableFrom(vehicle.getClass())) {
result.add(type.cast(vehicle)); // Compiler warning here
// Note, (T)vehicle generates an "Unchecked cast" warning (IDE can see this one)
}
}
return result;
}
我对实现这一点的任何其他方法都没有意见(我在集合
中找不到任何东西,但可能有一些JDK方法可以做到),但理想情况下,它将提供以下接口:
List<Car> cars = getVehiclesOfType(Car.class);
List cars=getVehiclesOfType(Car.class);
不过,我想知道为什么我在原始代码中收到编译器警告。这怎么样
if (type.isInstance(vehicle)) {
result.add((T)(vehicle));
}
编译器仍然这样抱怨吗
但如果我是你,我会使用,这将使你的方法成为一行:
public <T extends Vehicle> List<T> getVehiclesOfType(Class<T> type) {
return Lists.newArrayList(Iterables.filter(getVehicles(), type));
}
公共列表getVehiclesOfType(类类型){
return list.newArrayList(Iterables.filter(getVehicles(),type));
}
您可能需要将@SuppressWarning(“unchecked”)
添加到方法中,因为编译器(或IDE)无法在不理解isAssignableFrom()的含义的情况下知道强制转换是安全的,因此您会收到一条警告。但是isAssignableFrom()
不是一种语言特性,它只是一种库方法。就编译器而言,这和你说的一样
if (type.getName().contains("Elvis")) {
result.add(type.cast(vehicle));
}
但是,您知道isAssignableFrom()的意思,所以您知道它是安全的。这正是@SuppressWarnings
要解决的问题。问题在于编译器不够聪明,无法知道车辆属于“类型”。这是一个运行时检查,编译器不会进行这种分析。有很多这样的情况。例如,我使用if(true)返回;在调试过程中提前退出函数。如果使用justreturn,编译器会意识到存在无法访问的代码,但是使用条件,编译器不会意识到不可能进入该分支
考虑是否将条件替换为if(false){。代码没有引发异常的机会,但仍然包含不安全的强制转换
基本上,编译器会说,“我不能确认这是安全的,所以这取决于你是否知道你在做什么。”您的代码没有被破坏,您只需要小心操作。您在Vehicle上操作,Vehicle是一个超类,您试图向下转换为Vehicle的子类。这始终是一个不安全的转换,将为您赢得编译器警告
因此,虽然可以在没有警告的情况下从Car
转换为Vehicle
(因为Car扩展了Vehicle
),但编译器无法知道键入为Vehicle
的变量实际上是一辆车
通过使用cast(通过使用(Car)
或cast(…)
),您告诉编译器您知道得更好。
编译器讨厌人,但仍然向您发出警告:)就个人而言,我认为您采取了错误的方法。Java中的泛型是一种。您知道在编译时需要什么类型的列表,所以只需创建一个返回正确类型列表的帮助器方法:
public static List<Car> getCars( List<Vehicle> vlist ){ /* code here */ }
publicstaticlist-getCars(List-vlist){/*code-here*/}
将helper方法添加到相关类中,然后在代码中执行以下操作:
List<Car> cars = Cars.getCars( getVehicles() );
List cars=cars.getCars(getVehicles());
根本没有铸造问题。需要编写更多的代码,但也可以创建只返回蓝色汽车、红色汽车等的重载版本。2件事:
我想知道我为什么会这样
在上接收到编译器警告
不过是原始代码
第一:
看看类的代码:它确实为您隐藏了强制转换。通常,强制转换到任意类型(T)应该是一个警告,但是Class.cast实际上会检查并忽略编译器警告(无意义)
第二
这就是说:泛型警告是第一件要禁用的东西。混合使用旧代码和现在的代码不值得抑制警告,我也不在乎。我只是在等着看泛型如何减少ClassCastException,这可能只在一种情况下是正确的:使用add
而不是addAll
(put/putAll)这个问题的多个答案声称,警告是由于编译器无法静态验证强制转换不能失败。虽然编译器确实无法验证这一点,但没有理由给出警告!如果编译器会为无法静态验证的所有强制转换标记警告,那就是他是所有有意义的强制转换,因为当强制转换可以静态验证时,通常不需要首先执行强制转换。换句话说,强制转换操作的全部要点是它可能在运行时失败,这是一个非常好的情况,可以用类castexception
处理
“未经检查的演员阵容”另一方面,当您在运行时执行类型转换时,没有足够的类型信息来验证转换时,会发生警告。这可能是因为在运行时删除了类型参数。假设您将静态类型的List
转换为List
。在运行时,这两种类型的对象s只需将类ArrayList
或LinkedList
作为其唯一的运行时类型信息,而不带类型参数。因此,编译器无法插入任何代码,以在运行时验证该对象是否确实是车辆的列表。因此编译器不执行任何操作,而是发出“unchecked cast”(未检查强制转换)警告
这是一个有用的警告,因为当您开始使用强制转换的结果时,可能会遇到问题。由于结果具有静态类型列表
,因此您可以编写代码,将列表中的元素视为车辆
,而无需编写强制转换。但存在act
List<Car> cars = Cars.getCars( getVehicles() );
public T cast(Object obj) {
if (obj != null && !isInstance(obj))
throw new ClassCastException();
return (T) obj;
}