【问题标题】:Best way to avoid explicit casts避免显式强制转换的最佳方法
【发布时间】:2023-03-20 09:11:01
【问题描述】:

我有一个像下面这样的类层次结构

Vehicle
|_ TransaportationVehicle has method getLoadCapacity
|_ PassengerVehicle has method getPassengerCapacity

我还有一个类Booking 它引用了Vehicle

现在,每当我必须在 vehicle 引用上调用 getPassengerCapacitygetLoadCapacity 时,我需要将 cast vehicle 键入到它的具体实现中,例如 ((PassengerVehicle)vehicle).getPassengerCapacity(),并且这种类型的调用跨越项目中的多个部分。那么有什么方法可以避免这些类型的强制转换并且我的代码看起来既漂亮又干净?

注意:这些不是实际的类,我以这些为例来演示当前的问题。

【问题讨论】:

    标签: java inheritance design-patterns casting polymorphism


    【解决方案1】:

    显然,在预订Vehicle 时,您需要在某些时候区分它是TransportationVehicle 还是PassengerVehicle,因为它们具有不同的属性。

    最简单的方法是启动两个不同的Booking 进程:一个用于可以运输货物的车辆,一个用于可以运送乘客的车辆。至于如何区分这两种类型的车辆:您可以将canTransportPassengers()canTransportGoods() 方法添加到Vehicle,然后子类将覆盖这些方法以在适当的情况下返回true。此外,这样一来,可以同时运输两者的车辆是可能的,例如火车。

    【讨论】:

    • 这将如何避免类型转换工作?
    • 它将强制转换限制在您创建Booking 进程的单个点:它已经知道它需要TransportationVehicle 还是PassengerVehicle。或者,您可以将与容量相关的方法移动到 Vehicle 本身,而不关心任何子类,只关心它们的公开功能。
    【解决方案2】:

    如果您想使用不同的方法名称,那么您必须强制转换为具体类。

    但是,如果您可以使此方法返回相同的类型值并具有相同的名称,则可以对其使用多态性。在Vehicle 类中创建抽象方法并在每个子类中覆盖它。

    【讨论】:

      【解决方案3】:

      我将完成此操作的一种快速方法是创建一个 Generified Booking 父类。

      public abstract class Booking<V extends Vehicle> {
      
          protected abstract V getVehicle();
      }
      
      public class TransportationVehicleBooking extends Booking<TransaportationVehicle> {
      
          @Override
          protected TransaportationVehicle getVehicle() {
              return new TransaportationVehicle();
          }
      }
      
      public class PassengerVehicleBooking extends Booking<PassengerVehicle> {
      
          @Override
          protected PassengerVehicle getVehicle() {
              return new PassengerVehicle();
          }
      }
      

      您的Booking 类将具有跨越所有预订子类的所有逻辑以及每个子类进行有效计算所需的一些抽象方法。

      那么您所要做的就是引用Booking 类并调用所需的相关方法,而不必担心预订本身的“物流”(获取它)。

      我希望这会有所帮助。

      【讨论】:

        【解决方案4】:

        你的方法覆盖了概念。您需要在父类中拥有所有这些方法,并且可以在子类中覆盖这些方法。

        然后您可以使用运行时多态性访问超类中的所有方法

        车辆

        public interface Vehicle {
            public int getCapacity();
        }
        

        交通工具

        public class TransaportationVehicle implements Vehicle {
        
            @Override
            public int getCapacity() {
                return getLoadCapacity();
            }
        
            private int getLoadCapacity() {
                return 0;
            }
        
        }
        

        乘用车

        public class PassengerVehicle implements Vehicle {
        
            @Override
            public int getCapacity() {
                return getPassengerCapacity();
            }
        
            private int getPassengerCapacity() {
                return 0;
            }
        }
        

        用法

        Vehicle passenger = new PassengerVehicle();
        passenger.getCapacity();
        
        Vehicle transaportation = new TransaportationVehicle();
        transaportation.getCapacity()
        

        【讨论】:

        • 所以你的意思是我需要有一个方法来为层次结构中的所有类返回具体的类引用?
        • 否,您需要在父类中定义这些方法,并且应在子类中覆盖这些方法。
        • 好的,那么Bus 看到它是一种 可以同时容纳乘客和负载的运输工具,它会如何工作? AeroplaneCruiseBoat 等也是如此。
        【解决方案5】:

        首先尝试提取一个适用于所有车辆的抽象方法。如果你不能这样做,你也可以使用一个经常被遗忘的模式——visitor pattern。例如

        引入访问者界面

        public interface VehicleVisitor {
            public void visit(TransportationVehicle transportationVehicle);
            public void visit(PassengerVehicle passengerVehicle);
        }
        

        accept 方法添加到Vehicle

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

        在子类中实现accept方法

        public class PassengerVehicle implements Vehicle {
        
            private int passengerCapacity;
        
            public static PassengerVehicle withPassengerCapacity(int passengerCapacity) {
                return new PassengerVehicle(passengerCapacity);
        
            }
        
            private PassengerVehicle(int passengerCapacity) {
                this.passengerCapacity = passengerCapacity;
            }
        
            public int getPassengerCapacity() {
                return passengerCapacity;
            }
        
            @Override
            public void accept(VehicleVisitor visitor) {
                visitor.visit(this);
        
            }
        }
        
        public class TransportationVehicle implements Vehicle {
        
            private int loadCapacity;
        
            public static TransportationVehicle withLoadCapacity(int loadCapacity) {
                return new TransportationVehicle(loadCapacity);
        
            }
        
            private TransportationVehicle(int loadCapacity) {
                this.loadCapacity = loadCapacity;
            }
        
            public int getLoadCapacity() {
                return loadCapacity;
            }
        
            @Override
            public void accept(VehicleVisitor visitor) {
                visitor.visit(this);
            }
        }
        

        实现一个访问者...

        public class LoadSupported implements VehicleVisitor {
        
            private boolean supported;
            private int load;
        
            public LoadSupported(int load) {
                this.load = load;
            }
        
            public boolean isSupported() {
                return supported;
            }
        
            @Override
            public void visit(TransportationVehicle transportationVehicle) {
                int loadCapacity = transportationVehicle.getLoadCapacity();
                supported = load <= loadCapacity;
        
            }
        
            @Override
            public void visit(PassengerVehicle passengerVehicle) {
                supported = false;
            }
        
        }
        

        ...并使用它

        public class Main {
        
            public static void main(String[] args) {
                TransportationVehicle transportationVehicle1 = TransportationVehicle
                        .withLoadCapacity(5);
                TransportationVehicle transportationVehicle2 = TransportationVehicle
                        .withLoadCapacity(10);
                PassengerVehicle passengerVehicle = PassengerVehicle
                        .withPassengerCapacity(5);
        
                LoadSupported loadSupported = new LoadSupported(7);
        
                supportsLoad(transportationVehicle1, loadSupported);
                supportsLoad(transportationVehicle2, loadSupported);
                supportsLoad(passengerVehicle, loadSupported);
        
            }
        
            private static void supportsLoad(Vehicle vehicle,
                    LoadSupported loadSupported) {
                vehicle.accept(loadSupported);
                System.out.println(vehicle.getClass().getSimpleName() + "[" + System.identityHashCode(vehicle) + "]" + " does"
                        + (loadSupported.isSupported() ? " " : " not ")
                        + "support load capacity");
            }
        }
        

        输出会是这样的

        TransportationVehicle[778966024] does not support load capacity
        TransportationVehicle[1021653256] does support load capacity
        PassengerVehicle[1794515827] does not support load capacity
        

        【讨论】:

          【解决方案6】:

          假设载客量始终是一个整数,而载客量很可能是一个很大的数字,具体取决于载客量的单位。我会继续创建 Vehicle 类,如下所示:

          class Vehicle {
              Number capacity; 
              public Number getCapacity() {
                  return capacity; 
              }
              public void setCapacity(Number capacity) {
                  this.capacity = capacity; 
              }
          }
          

          我使用Number 的原因是我在PassengerVehicle 类中使用Integer,在TransporatationVehicle 中使用Double,这是因为IntegerDoubleNumber 的子类型,你可以得到用演员表离开。

          class TransportationVehicle extends Vehicle {
              @Override 
              public Double getCapacity() {
                  //all I have to do is cast Number to Double
                  return (Double) capacity; 
              }
          
              @Override 
              public void setCapacity(Number capacity) {
                  this.capacity = capacity; 
              }
          }
          

          PassengerVehicle 类如下:

          class PassengerVehicle extends Vehicle {
              @Override 
              public Integer getCapacity() {
                  //Cast to Integer and works because Integer is subtype of Number
                  return (Integer) capacity; 
              }
          
              @Override 
              public void setCapacity(Number capacity) {
                  this.capacity = capacity; 
              }
          }
          

          然后您可以使用上面的类来创建车辆对象,如下所示:

          public class Booking {
              public static void main(String[] args) {
                  //
                  Vehicle transportationVehicle = new TransportationVehicle();
                  //assigning Double to setCapacity
                  transportationVehicle.setCapacity(new Double(225.12));
          
          
                  Vehicle passengerVehicle = new PassengerVehicle(); 
                  //assigning Integer to setCapacity
                  passengerVehicle.setCapacity(5);
          
                  System.out.println(transportationVehicle.getCapacity()); 
                  // output: 225.12
                  System.out.println(passengerVehicle.getCapacity());
                  // output: 5
              }
          }
          

          在旁注中,如果您尝试传递 TransportationVehicle 除了 Number 或 Double 之外的任何内容,那么您将获得异常,同样如果您传递 PassengerVehicle 除了 Number 或 Integer 之外的任何内容,您将获得异常。

          我知道我偏离了您的问题范围,但是,我真的想展示如何使您的方法成为泛型。这允许您决定在编码期间返回getCapacity() 的类型,这非常灵活。见下文:

          class Vehicle<T> {
              //generic type T
              T capacity; 
          
              //generic method getCapacity
              public T getCapacity() {
                  return capacity; 
              }
          
              //generic method setCapacity 
              public void setCapacity(T capacity) {
                  this.capacity = capacity; 
              }
          }
          
          class TransportationVehicle<T> extends Vehicle<T> {
              @Override 
              public T getCapacity() {
                  return capacity; 
              }
          
              @Override 
              public void setCapacity(T capacity) {
                  this.capacity = capacity; 
              }
          }
          
          class PassengerVehicle<T> extends Vehicle<T> {
              @Override 
              public T getCapacity() {
                  return capacity; 
              }
          
              @Override 
              public void setCapacity(T capacity) {
                  this.capacity = capacity; 
              }
          }
          

          正如您在上面看到的通用方法,您可以按如下方式使用它们:

          Vehicle<String> vehicleString = new TransportationVehicle<String>();
          vehicleString.setCapacity("Seriously!"); //no problem
          
          Vehicle<Integer> vehicleInteger = new PassengerVehicle<Integer>(); 
          vehicleInteger.setCapacity(3); //boxing done automatically
          
          Vehicle<Double> vehicleDouble = new PassengerVehicle<Double>(); 
          vehicleDouble.setCapacity(2.2); //boxing done automatically  
          

          您可以在编码时决定类型,如果您提供容量为IntegerVehicle&lt;String&gt;,那么您将收到编译时错误,因此您将不被允许。

          System.out.println(vehicleString.getCapacity()); 
          //output: Seriously!
          System.out.println(vehicleInteger.getCapacity());
          //output:  3 
          System.out.println(vehicleDouble.getCapacity());
          //output: 2.2 
          

          【讨论】:

            【解决方案7】:

            我不明白这个例子。您如何意识到您首先要处理的是具体类型?你是instanceOf-ing吗?你的类型匹配吗?

            如果是这样,您的问题已经超出了投射范围...

            无论如何,当您拥有必须属于同一家族的对象和非抽象算法并且根据正在处理的对象而变化时,您通常会使用某种behavioral pattern,例如visitor,或Bridge pattern

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2015-05-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多