假设您有一个Shape
基类和各种派生类型:Circle
等。
在制作新对象时,是否有任何理由在此处向上转换,通过编写以下代码:
Shape s = new Circle();
而不是这个:
Circle s = new Circle();
这两个陈述中的每一个所产生的s
对象是否彼此不同?
您可以争辩说,您的第一个示例(即Shape s = new Circle();
)可以具有与“对接口进行编码”相同的优势,即使 Shape 可能是抽象的甚至是具体的基类。因此,例如,如果您只使用 Shape 上定义的方法而不是 Circle 特定的方法,那么您可以很容易地将您正在使用的实现更改为 Square,例如只需更改一行代码,即Shape s = new Square();
.
您的两个示例中的对象都是相同的,可以认为第一个选项更好的原因更多是一种风格。对接口进行编码可以使代码库更容易扩展和修改。
就 JVM 而言,这两个语句将产生相同的对象。表明您只打算将对象用于基类或接口的情况并不少见。例如,这是常见的:
List<String> list = new ArrayList<String>();
虽然通常不是一个好主意,但如果您确定它是一个,您可以将其转换为 a,例如,Shape
您可以将它带回 a :Circle
Shape s
Circle c
if (s instanceof Circle) {
Circle c = (Circle) s;
// handle Circle case
}
实例化的对象完全相同。
向上转型的后果是:
在回答您的问题时,这两个对象完全相同。两个引用都可以指向同一个对象:
Circle c = new Circle();
Shape s = c;
对接口进行编码允许您在对代码的影响最小的情况下更改实现类。假设你有这个:
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。因为您已经对接口进行了编码并且没有使用任何特定于 HashSet 类的方法,所以您只需更改一行:
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");
}
接口还允许您使用依赖注入。它允许您使用在编写代码时甚至可能不存在的类。
我同意@WhiteFang34 所说的。
看到这个Coding to interfaces 了吗? 这可能会帮助你。
不,对象本身并没有什么不同。只有编译器认为他看到的不同(例如哪些方法可用)。
进行这样的强制转换的一个原因是表达您喜欢如何对待对象(以便您可以轻松地插入不同的派生类)。
圆 s = 新圆();
这里的对象是指类 Cricle
形状 s = new Circle(); 这里的对象是指接口
两者都是一样的。