关于java:制作对象时的向上转换

Upcasting when making object

假设您有一个Shape基类和各种派生类型:Circle等。

在制作新物体时,是否有任何理由向上投射,方法如下:

1
Shape s = new Circle();

而不是这个:

1
Circle s = new Circle();

这两个说法中的每一个所提出的s物体有什么不同吗?


您可以认为,您的第一个示例(即Shape s = new Circle();)与"接口编码"具有相同的优势,即使形状可能是抽象的,甚至是具体的基类。例如,如果您只使用在shape上定义的方法,而不使用那些特定于circle的方法,那么您可以很容易地将正在使用的实现更改为正方形,例如只更改一行代码,即Shape s = new Square();

两个例子中的对象都是相同的,第一个选项被认为更好的原因更多的是一种样式。对接口进行编码可以使代码库更容易扩展和修改。


就JVM而言,这两个语句将产生相同的对象。通常情况下,您只计划将对象用于基类或接口。例如,这是常见的:

1
List<String> list = new ArrayList<String>();

虽然通常不是一个好主意,但如果您确定它是一个,您可以将Shape重新转换成Circle,例如,使用Shape s,您可以使用以下方法将它恢复到Circle c

1
2
3
4
if (s instanceof Circle) {
    Circle c = (Circle) s;
    // handle Circle case
}


为了回答你的问题,这两个物体是完全相同的。两个引用都可以指向同一个对象:

1
2
Circle c = new Circle();
Shape s = c;

通过对接口进行编码,您可以在对代码影响最小的情况下更改实现类。假设你有这个:

1
2
3
4
5
6
7
8
Set<String> names = new HashSet<String>();         // using Set reference not HashSet
names.add("Frank");
names.add("John");
names.add("Larry");

for(String name: names) {
    System.out.println(name +" is in the team");
}

现在您的需求发生了变化,您希望按字母顺序打印名称,即使用hashset而不是treeset。因为您已经对接口进行了编码,并且没有使用任何特定于哈希集类的方法,所以只需更改一行:

1
2
3
4
5
6
7
8
Set<String> names = new TreeSet<String>();   // the only line of code that changes
names.add("Frank");
names.add("John");
names.add("Larry");

for(String name: names) {
    System.out.println(name +" is in the team");
}

接口还允许您使用依赖注入。它允许您使用在编写代码时甚至不存在的类。

http://en.wikipedia.org/wiki/dependency_注入


实例化的对象完全相同。

上抛的后果是:

  • 存储时的泛化:所有不同专门类型的对象都可以作为同一接口进行线程化,并可以存储在数据结构中。(如@whitefang34:list指出的)
  • 访问对象方法时的一般化:客户端类可以调用shape的方法,而不知道实际类型(通常可以使用工厂创建返回公共接口形状的对象,因此客户端类不知道对象的实际类型,但只公开形状的接口方法)

  • 我同意@whitefang34的说法。

    看到这个接口编码了吗?这可能对你有帮助。


    不,对象本身没有不同。只有编译器认为他看到的是不同的(例如哪些方法可用)。

    这样进行强制转换的一个原因是表示您希望如何处理对象(这样您就可以轻松地插入不同的派生类)。


    圆S=新圆();这里的物体是指阶级的十字线。

    形状S=新圆();这里的对象引用接口

    两者相同。