首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用泛型时避免铸造

使用泛型时避免铸造
EN

Stack Overflow用户
提问于 2015-02-08 11:56:39
回答 3查看 3.1K关注 0票数 4

在一个项目中,我有一个使用该服务的服务和一个类。在本例中,车辆将使用的维修服务。维修服务只能修理某一类型的车辆:车库只能修理汽车。我需要一种方法在车辆上修理它与一个适用的服务,repairUsingService(..)

我的目标是拥有一个干净的Vehicle基类和干净的RepairService实现。我尝试过两种方法来设计维修服务的repair方法:

  • repair(Vehicle<T> vehicle):这很难看,因为实现需要执行repair(Vehicle<Car> car),但是很明显,汽车就是车辆。
  • repairSimple(T vehicle):这样做很不错,但是如果没有难看的角色,就不能从Vehicle类中调用。

是否有一种避免强制转换的方法,但仍然只使用泛型参数类型T (如repairSimple(T))中的)

代码语言:javascript
复制
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.");
    }
}
EN

回答 3

Stack Overflow用户

发布于 2015-02-08 13:20:26

您能使用这个实现吗?通过这种方式,车辆知道什么维修服务可以修理它,服务知道什么车辆可以修理。

代码语言:javascript
复制
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());

    }

}

您甚至可以为您的所有修复服务创建一个基类,以避免代码重复:

代码语言:javascript
复制
public abstract class BaseRepairService<T extends Vehicle<?>> implements 
RepairService<T> {

    @Override
    public void repair(T vehicle) {

    }

}

然后,您的车库将扩展一个带有汽车类型参数的BaseRepairService。

票数 5
EN

Stack Overflow用户

发布于 2015-02-08 12:50:07

一种方法是自问子类:

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2015-02-09 05:43:11

让我提出两个合理的方案。

第一个是加弗氏小工具的变体。

代码语言:javascript
复制
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可以修复。

代码语言:javascript
复制
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可能也不再需要类型参数了。

也许您的实际程序是不同的,但是在引入参数化设计之前,您应该始终考虑直接的程序逻辑是否解决了这个问题。试图强迫泛型在它们是次优解决方案的情况下工作是非常尴尬的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28393626

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档