提问者:小点点

Java 8流中引用具有特定属性的特定子类型的筛选器元素


我有一个汽车列表,其中每辆汽车都有一个发动机,该发动机是通过界面定义的。在本例中,具体类型为具有可变气缸数的燃烧发动机和电动机。

我想找到所有四缸的(内燃机)发动机。使用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 );
  }
}

共1个答案

匿名用户

我认为避免演员阵容是不可能的。毕竟,无论是汽车还是发动机,都没有提供任何方法来区分电动汽车和内燃机汽车。

但是,如果您的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());

但我不确定这是否更具可读性。