Interface Methods with Variable Argument Types

I have a Java interface and a class implementation that need different arguments when invoking this behavior. Which of the following is mostly suitable?

In the first version, I have different classes that inherit the general behavior from the base interface, and all the differences are implemented directly in the classes, and not in the interface. This seems most appropriate, but I have to do some manual text input in the code.

public class VaryParam1 {

    static Map<VehicleType, Vehicle> list = new HashMap<>();

    static List<Car> carsList = new ArrayList<>();
    static List<TruckWithTrailer> trucksList = new ArrayList<>();

    public static void main(String[] args) {
        list.put(VehicleType.WITHOUT_TRAILER, new Car());
        list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());

        //violates LSP?
        ((Car)list.get(VehicleType.WITHOUT_TRAILER)).paint(1); //ok - but needed manual cast
        ((TruckWithTrailer)list.get(VehicleType.WITH_TRAILER)).paint(1, "1"); //ok - but needed manual cast

        carsList.add(new Car());
        trucksList.add(new TruckWithTrailer());

        //Does not violate LSP
        carsList.get(0).paint(1);
        trucksList.get(0).paint(1, "1");
    }
}

enum VehicleType {
    WITHOUT_TRAILER,
    WITH_TRAILER;
}

interface Vehicle{
    //definition of all common methods
    void drive();
    void stop();
}

class Car implements Vehicle {

    public void paint(int vehicleColor) {
        System.out.println(vehicleColor);
    }

    @Override
    public void drive() {}

    @Override
    public void stop() {}
}

class TruckWithTrailer implements Vehicle {

    public void paint(int vehicleColor, String trailerColor) {
        System.out.println(vehicleColor + trailerColor);
    }

    @Override
    public void drive() {}

    @Override
    public void stop() {}
}

, UnsupportedOpException. . , , - . - , . ?

public class VaryParam2 {

    static Map<VehicleType, Vehicle> list = new HashMap<>();

    public static void main(String[] args) {
        list.put(VehicleType.WITHOUT_TRAILER, new Car());
        list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());

        list.get(VehicleType.WITHOUT_TRAILER).paint(1); //works
        list.get(VehicleType.WITH_TRAILER).paint(1, "1"); //works

        list.get(VehicleType.WITHOUT_TRAILER).paint(1, "1"); //ok - exception - passing trailer when no trailer - no compile time check!
        list.get(VehicleType.WITH_TRAILER).paint(1); //ok - exception - calling trailer without trailer args - no compile time check!
    }
}

enum VehicleType {
    WITHOUT_TRAILER,
    WITH_TRAILER;
}

interface Vehicle{
    void paint(int vehicleColor);
    void paint(int vehicleColor, String trailerColor);    //code smell - not valid for all vehicles??
}

class Car implements Vehicle {

    @Override
    public void paint(int vehicleColor) {
        System.out.println(vehicleColor);
    }

    @Override
    public void paint(int vehicleColor, String trailerColor) {    //code smell ??
        throw new UnsupportedOperationException("Car has no trailer");
    }
}

class TruckWithTrailer implements Vehicle {

    @Override
    public void paint(int vehicleColor) {  //code smell ??
        throw new UnsupportedOperationException("What to do with the trailer?");
    }

    @Override
    public void paint(int vehicleColor, String trailerColor) {
        System.out.println(vehicleColor + trailerColor);
    }
}

generics , , . , . 1. , !

public class VaryParam3 {

    static Map<VehicleType, Vehicle> list = new HashMap<>();


    public static void main(String[] args) {
        list.put(VehicleType.WITHOUT_TRAILER, new Car());
        list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());

        list.get(VehicleType.WITHOUT_TRAILER).paint(new VehicleParam());    //works but unchecked call
        list.get(VehicleType.WITH_TRAILER).paint(new TruckWithTrailerParam());    //works but unchecked call

        list.get(VehicleType.WITHOUT_TRAILER).paint(new TruckWithTrailerParam()); //works but should not!
        list.get(VehicleType.WITH_TRAILER).paint(new VehicleParam());   //ClassCastException in runtime - ok but no compile time check
    }
}

enum VehicleType {
    WITHOUT_TRAILER,
    WITH_TRAILER;
}

class VehicleParam {
    int vehicleColor;
}

class TruckWithTrailerParam extends VehicleParam {
    String trailerColor;
}

interface Vehicle<T extends VehicleParam>{
    void paint(T param);
}

class Car implements Vehicle<VehicleParam> {

    @Override
    public void paint(VehicleParam param) {
        System.out.println(param.vehicleColor);
    }
}

class TruckWithTrailer implements Vehicle<TruckWithTrailerParam> {

    @Override
    public void paint(TruckWithTrailerParam param) {
        System.out.println(param.vehicleColor + param.trailerColor);
    }
}

, , 3 ( , )? , ..

UPDATE

, paint, , .

, :

public class VaryParam4 {

    static Map<VehicleType, Vehicle> list = new HashMap<>();

    public static void main(String[] args) {
        list.put(VehicleType.WITHOUT_TRAILER, new Car());
        list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());

        list.get(VehicleType.WITHOUT_TRAILER).paint(new PaintConfigObject());    //works but can pass trailerColor (even if null) that is not needed
        list.get(VehicleType.WITH_TRAILER).paint(new PaintConfigObject());    //works
    }
}

enum VehicleType {
    WITHOUT_TRAILER,
    WITH_TRAILER;
}

class PaintConfigObject {
    int vehicleColor;
    String trailerColor;
}

interface Vehicle{
    void paint(PaintConfigObject param);
}

class Car implements Vehicle {

    @Override
    public void paint(PaintConfigObject param) {
        //param.trailerColor will never be used here but it passed in param
        System.out.println(param.vehicleColor);
    }
}

class TruckWithTrailer implements Vehicle {

    @Override
    public void paint(PaintConfigObject param) {
        System.out.println(param.vehicleColor + param.trailerColor);
    }
}
+4
2

drive , , :

interface Vehicle{
    void drive();
}

class Car implements Vehicle {
    private int numberOfDoors;

    public Car(int numberOfDoors) {
         this.numberOfDoors = numberOfDoors;
     }

    public void drive() {
        System.out.println(numberOfDoors);
    }
}


class TruckWithTrailer implements Vehicle {
    private int numberOfDoors;
    private int numberOfTrailers;

    public TruckWithTrailer(int numberOfDoors,numberOfTrailers) {
          this.numberOfDoors = numberOfDoors;
          this.numberOfTrailers = numberOfTrailers;
    }

    @Override
    public void drive() {
        System.out.println(numberOfDoors + numberOfTrailers);
    }
}

paint, , paint , :

interface Vehicle{
    void drive();
    void paint(String ...colors);
}

, , , , PaintSpecification, , vehcileColor, trailerColor paint, PaintSpecification.

interface Vehicle{
    void drive();
    void paint(PaintSpecification spec);
}

, Vehicle , , Vehicle List paint .

+8

.

, , .

, .

Vehicle, Vehicle , . .

public void paint();

, Vehicle , . , color .

public class Car implements Vehicle {

  private int color = 0; // Default color

  public void paint() {
    System.out.println(color);
  }

  public void setColor(int color){
     // maybe some validation first
     this.color = color;
  }
}

?

, - .

:

  • instanceof downcast ( )
  • -
  • Visitor-Pattern

-

interface Vehicle {
    public <T extends Vehicle> T getAdapter(Class<T> adapterClass);
}

class Car implements Vehicle {

    @Override
    public <T extends Vehicle> T getAdapter(Class<T> adapterClass) {
        if(adapterClass.isInstance(this)){
            return adapterClass.cast(this);
        }
        return null;
    }
}

:

Vehicle vehicle = ...;

Car car = vehicle.getAdapter(Car.class);
if(car != null){
    // the vehicle can be adapted to a car
    car.paint(1);
}

  • instanceof . , . . :

    if(vehicle instanceof Car){
       // ...
    } else if(vehicle instanceof TruckWithTrailer){
       // ...
    }
    

    , , TruckWithTrailer extends Car

  • . Vehicle , .

    public <T extends Vehicle> T getAdapter(Class<T> adapterClass) {
        if(Car.class.isAssignableFrom(adapterClass)){
            return new CarAdapter(this)
        }
        return null;
    }
    

  • , Vehicle ( if-else).

Visitor-

interface Vehicle {
    public void accept(VehicleVisitor vehicleVisitor);
}

interface VehicleVisitor {
    public void visit(Car car);
    public void visit(TruckWithTrailer truckWithTrailer);
}

, VihicleVisitor .

class Car implements Vehicle {

    public void paint(int vehicleColor) {
        System.out.println(vehicleColor);
    }

    @Override
    public void accept(VehicleVisitor vehicleVisitor) {
        vehicleVisitor.visit(this);
    }
}

VehicleVisitor

    Vehicle vehicle = ...;
    vehicle.accept(new VehicleVisitor() {

        public void visit(TruckWithTrailer truckWithTrailer) {
            truckWithTrailer.paint(1, "1");

        }

        public void visit(Car car) {
            car.paint(1);
        }
    });

  • , .

PS: .

+1

Source: https://habr.com/ru/post/1670174/


All Articles