2015-02-08 48 views
4

在一個項目中,我有一個服務和一個使用該服務的類。在這個例子中,車輛將使用維修服務。維修服務只能修理某種類型的車輛:車庫只能修理汽車。我需要在車輛上使用適用的服務修理它,repairUsingService(..)使用泛型時避免投射

我的目標是有一個乾淨的Vehicle基類和清潔RepairService實現。我試圖設計維修服務的repair方法有兩種方法:

  • repair(Vehicle<T> vehicle):這是醜陋的,因爲實現需要做repair(Vehicle<Car> car),但很顯然,汽車是車輛。
  • repairSimple(T vehicle):是與漂亮的,但不能從Vehicle類叫做沒有一個醜陋的演員。

有沒有辦法避免鑄造,但仍只使用泛型參數類型(repairSimple(T)等)T

public class Vehicle<T extends Vehicle<T>> { 

    public void repairUsingService(RepairService<T> obj) { 
     obj.repair(this); 
     obj.repairSimple((T) this); 
    } 
} 

public class Car extends Vehicle<Car> { 
} 

public interface RepairService<T extends Vehicle<T>> { 

    void repair(Vehicle<T> vehicle); 

    void repairSimple(T vehicle); 
} 

public class Garage implements RepairService<Car> { 
    @Override 
    public void repair(Vehicle<Car> car) { 
     System.out.println("Car repaired."); 
    } 

    @Override 
    public void repairSimple(Car car) { 
     System.out.println("Car repaired."); 
    } 
} 
+7

是否有真正的原因,因爲'class Vehicle >'因此'class Car extend Vehicle ''而不是'class Car extend Vehicle'? – 2015-02-08 12:00:39

+0

不能僅僅使用基類來做一些事情,然後在實現類中重寫它呢? – engineercoding 2015-02-08 12:23:36

+0

@MikeNakis它用於像'repairUsingService(RepairService )'這樣的方法和變量,它需要車輛的通用類型。或者是否有避免這種情況的好方法? – Frithjof 2015-02-08 12:25:20

回答

2

一種方法是要求子類本身:

abstract class Vehicle<T extends Vehicle<T>> { 

    public void repairUsingService(RepairService<T> obj) { 
     obj.repair(this); 
     obj.repairSimple(getThis()); 
    } 
    abstract T getThis(); 
} 

class Car extends Vehicle<Car> { 
    @Override 
    Car getThis(){ 
     return this; 
    } 
} 
2

您可以使用此實現?通過這種方式,車輛都知道,哪些維修服務可以修理它,並且服務知道,它可以修理哪些車輛。

public interface RepairService<T extends Vehicle<?>> { 
    public void repair(T vehicle); 
} 

public interface Vehicle<T extends RepairService<?>> { 
    public void repairUsingService(T service); 
} 

public class Car implements Vehicle<Garage> { 
    @Override 
    public void repairUsingService(Garage service) { 
    } 
} 

public class Garage implements RepairService<Car>{ 
    @Override 
    public void repair(Car vehicle) { 
    } 
} 

public class AuthorizedGarage extends Garage { 

} 


public class Train implements Vehicle<TrainDepot> { 
    @Override 
    public void repairUsingService(TrainDepot service) { 

    } 

} 

public class TrainDepot implements RepairService<Train> { 
    @Override 
    public void repair(Train vehicle) { 

    } 

} 

public class Test { 

    public static void main(String[] args) { 

     // this works: 
     new Car().repairUsingService(new Garage()); 
     new Train().repairUsingService(new TrainDepot()); 

     // and this works 
     new Garage().repair(new Car()); 
     new TrainDepot().repair(new Train()); 

     // but this does not (which is ok) 
     //new Garage().repair(new Train()); 
     //new Car().repairUsingService(new TrainDepot()); 

     // this also works 
     List<Car> cars = new ArrayList<>(); 
     cars.add(new Car()); 
     cars.get(0).repairUsingService(new Garage()); 

     // this also works, if you have an expensive car ;) 
     new Car().repairUsingService(new AuthorizedGarage()); 

    } 

} 

你甚至可以有一個基類的所有維修服務,以避免重複代碼:

public abstract class BaseRepairService<T extends Vehicle<?>> implements 
RepairService<T> { 

    @Override 
    public void repair(T vehicle) { 

    } 

} 

那麼你的車庫會延長BaseRepairService與汽車類型參數。

1

讓我目前的兩個合理的替代方案。


首先是Gafter's Gadget變化:

public abstract class Vehicle<V extends Vehicle<V>> { 
    private boolean validate() { 
     Class<?> cls = getClass(); 
     for(Class<?> sup; 
      (sup = cls.getSuperclass()) != Vehicle.class; 
      cls = sup 
     ); 
     Type sup = cls.getGenericSuperclass(); 
     if(!(sup instanceof ParameterizedType)) 
      return false; 
     Type arg = ((ParameterizedType)sup).getActualTypeArguments()[0]; 
     if(!(arg instanceof Class<?>)) 
      return false; 
     return ((Class<?>)arg).isInstance(this); 
    } 

    protected Vehicle() { 
     assert validate() : "somebody messed up"; 
    } 
} 

由於Vehicle總是由一個子類參數,這是確定使用這個成語。在開發過程中,如果有人不正確地擴展類,構造函數會拋出一個錯誤。

現在選中投始終是安全的。


第二是RepairService不再攜帶類型參數。相反,你保持Class<? extends Vehicle>的上市RepairService可以修復。

public interface RepairService { 
    boolean canRepair(Vehicle v); 
    // if v can't be repaired, perhaps repair 
    // throws an exception or returns boolean instead of void 
    void repair(Vehicle v); 
} 

public class ServiceStation implements RepairService { 
    private final List<Class<? extends Vehicle>> types; 

    public ServiceStation(Class<? extends Vehicle>... types) { 
     this.types = Arrays.asList(types); 
    } 

    @Override 
    public boolean canRepair(Vehicle v) { 
     for(Class<? extends Vehicle> c : types) { 
      if(c.isInstance(v)) 
       return true; 
     } 
     return false; 
    } 

    @Override 
    public void repair(Vehicle v) { 
     if(!canRepair(v)) 
      throw new IllegalArgumentException(); 
     // impl 
    } 
} 

至少在Vehicle/RepairStation比喻這可能是比試圖強行仿製藥的設計更有用。 Vehicle可能不再需要類型參數。

也許你的實際程序是不同的,但你應該總是考慮直線程序邏輯在引入參數化設計之前是否解決了這個問題。試圖迫使仿製藥在他們不是最理想的解決方案的情況下工作變得非常尷尬。