过滤来自Java 8流的元素,该元素引用具有特定属性的特定子类型

时间:2015-12-04 22:05:24

标签: java-stream

我有一个Car列表,其中每辆车都有一个通过Engine界面定义的引擎。在此示例中,具体类型为CombustionEngine,具有可变数量的柱面,并且ElectricMotor

我想找到所有(燃烧)发动机有四个气缸。使用Java流我想出了这个管道:

Car[] carsWithFourCylinders
  = cars.stream()
  .filter( car -> car.engine instanceof CombustionEngine )
  .filter( car -> ( ( CombustionEngine )car.engine ).cylinderCount == 4 )
  .toArray( Car[]::new );

虽然这有效,我想知道是否可以避免第二个filter谓词中的强制转换或者完全重写管道以便更具可读性?

供参考,为了体验我已附上该示例的完整来源:

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 个答案:

答案 0 :(得分:1)

我认为没有可能避免演员表演。毕竟,CarEngine都没有提供任何可以区分电动汽车和带有ICE的方法。

但如果你的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());

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