我有一个汽车列表,其中每辆汽车都有一个发动机,该发动机是通过界面定义的。在本例中,具体类型为具有可变气缸数的燃烧发动机和电动机。
我想找到所有四缸的(内燃机)发动机。使用Java流,我想出了这个管道:
Car[] carsWithFourCylinders
= cars.stream()
.filter( car -> car.engine instanceof CombustionEngine )
.filter( car -> ( ( CombustionEngine )car.engine ).cylinderCount == 4 )
.toArray( Car[]::new );
虽然这样做可行,但我想知道是否可以避免在第二个过滤器谓词中进行强制转换,或者完全重写管道以提高可读性?
为了便于参考和体验,我附上了示例的完整来源:
public class CarTest {
interface Engine { }
class CombustionEngine implements Engine {
final int cylinderCount;
CombustionEngine( int cylinderCount ) {
this.cylinderCount = cylinderCount;
}
}
class ElectricMotor implements Engine { }
class Car {
final Engine engine;
Car( Engine engine ) {
this.engine = engine;
}
}
@Test
public void filterCarsWithFourCylinders() {
List<Car> cars = Arrays.asList( new Car( new CombustionEngine( 4 ) ),
new Car( new ElectricMotor() ),
new Car( new CombustionEngine( 6 ) ) );
Car[] carsWithFourCylinders
= cars.stream()
.filter( car -> car.engine instanceof CombustionEngine )
.filter( car -> ( ( CombustionEngine )car.engine ).cylinderCount == 4 )
.toArray( Car[]::new );
assertEquals( 1, carsWithFourCylinders.length );
}
}
我认为避免演员阵容是不可能的。毕竟,无论是汽车还是发动机,都没有提供任何方法来区分电动汽车和内燃机汽车。
但是,如果您的Engine
没有方法,在我看来,这意味着它应该与Car
它拥有什么样的引擎无关。
我能想到的最好的办法就是
final List<Car> combustionCars = cars.stream()
.collect(groupingBy(c -> c.engine.getClass()))
.get(CombustionEngine.class);
long count = combustionCars
.stream()
.map(Car::getEngine)
.map(CombustionEngine.class::cast)
.filter(c -> c.cylinderCount == 4).collect(Collectors.counting());
但我不确定这是否更具可读性。