2013-10-09 116 views
1

考虑以下情形很常见的OO文献:超类返回子类型

public class Vehicle { 
} 

public class Car extends Vehicle { 
} 

public class Bike extends Vehicle { 
} 

现在,假设我想创建一个功能get()将始终返回子类型,以便我能得到一个子类的实例:

public static void main(String[] args) { 

    Car car = Car.get(); 
    Bike bike = Bike.get(); 
    car.Start(); 
    bike.Start(); 
} 

它可以实现在意识到它的返回类型的超类public static <T> T get()没有传递给它作为一个参数或从Object铸造?

UPDATE:

在我说的是抽象类的第一个版本。现在我删除了摘要,并使用常规课程。

+0

我不知道Java是否支持[CRTP](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) –

+0

为什么要这么做?抽象类的全部要点是隐藏具体细节。 – dbarnes

+1

为什么你不说:'汽车=新车();'......等等? –

回答

1

在这种情况下,如果您在超类上调用静态方法,Java不知道您想要返回哪种类型的子类。你提到过你不想传入参数就可以做到,但不能。如果你能在一个参数传递,这是你会怎么做:

package com.stackexchange.stackoverflow; 

public class Question19281170 { 
    public static void main(String[] args) throws InstantiationException, IllegalAccessException { 
    Car car = Vehicle.get(Car.class); 
    Bike bike = Bike.get(Bike.class); 
    car.start(); 
    bike.start(); 
    } 

    static abstract class Vehicle { 
    public void start() { 
     System.out.println(String.format("Start %s", toString())); 
    } 
    public static <T extends Vehicle> T get(Class<T> clazz) throws IllegalAccessException, InstantiationException { 
     return clazz.newInstance(); 
    } 
    @Override 
    public String toString() { 
     return "Vehicle"; 
    } 
    } 

    static class Car extends Vehicle { 
    @Override 
    public String toString() { 
     return "Car"; 
    } 
    } 

    static class Bike extends Vehicle { 
    @Override 
    public String toString() { 
     return "Bike"; 
    } 
    } 
} 

这种情况的最佳做法是只使用多态:

Vehicle car = new Car(); 
Vehicle bike = new Bike(); 
+0

'main'中的代码可能就是'Bike bike = Vehicle.get(Bike.class)'。 –

+0

是的,这在前面的行中说明:Car car = Vehicle.get(Car.class);我只是想演示两种方法。事实上,如果传递参数是一个好的解决方案,泛型可以从超类中移除,并且可以使用具有任何通用get方法的简单实用程序类。这似乎毫无意义。 – axiopisty

0

继承是一种强大的功能和良好适合构建基于类的层次结构,如JavaFX中的层次结构。

但是,设计继承类是一个痛苦和充满潜在的问题。以下是一个常见问题:如果您决定使用HashMap来表示车辆集合,该怎么办?问题在于,以不打破equals()和hashcode()的契约的方式编写子类的equals()和hashcode()方法比听起来难。此外,继承会破坏封装并使您的子类依赖于超类中的实现细节,这会使代码变得脆弱或难以维护。

继承的替代方法是使用基于接口的类型系统,类似于Java Collections API使用的系统。

public interface Vehicle { 
    public void start(); 
    : 
} 

public class Car implements Vehicle { 
} 

public class Bike implements Vehicle { 
} 

该接口用作基本类型,具有几个优点。最重要的是,任何实现Vehicle接口的类都可以传递给请求Vehicle的任何方法,而不管类是什么。虽然这看起来类似于继承,但没有实际的基于类的层次结构。实现细节,封装和equals()合同的所有问题都会消失。

到基于接口的类型系统的缺点是几种:

  • 你必须要能够稳健地指定的基本类型与接口定义的行为。如果不存在这种情况,则作为基本类型的接口变得不方便。

  • 接口必须由每个类使用它完全实现(除非您为接口创建抽象骨架实现类,例如Collections API中的AbstractList)。

  • 它很难改变接口......但编辑超类常常是个问题。请记住,对超类的更改可能会在其子类中产生副作用,这些副作用取决于超类中的实现细节。

有一个基于接口的类型,使您的情况是这个样子(静态工厂使用):

Vehicle car = Car.newInstance(); 
Vehicle bike = Bike.newInstance(); 

car.start(); 
bike.start(); 

TL; DR:基于接口的类型系统是解决的问题非常有用创建一个基于类的类型系统(继承破解封装),并且可以成为某些问题的很好的替代解决方案。