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;
}