为什么显式实现后返回的是list接口,而不是ArrayList接口实现
Why list interface is returned instead of ArrayList interface implementation after explicit implementation
我知道我的代码内部发生了什么,也知道如何修复错误。但是,我不明白为什么事情会这样发生。具体来说,为什么我的方法 returns interface List 而不是 ArrayList 接口实现?
最让我困惑的是在将 moons
实现为 ArrayList
之后,它仍然是 returns 接口。
提前致谢!
private static ArrayList<HeavenlyBody> makingSetOfMoons(){
List<HeavenlyBody> moons = new ArrayList<>();
for(HeavenlyBody planet : Main.planets){
moons.addAll(planet.getSatellites());
}
return moons;
}
错误输出:
Incompatible types. Found: 'java.util.List<com.practice.HeavenlyBody>', required: 'java.util.ArrayList<com.practice.HeavenlyBody>'
编译器期望 return 类型 ArrayList
或更具体类型的对象(扩展 ArrayList
的 class )的方法。但是,从该方法中编辑的 moons
变量 return 的类型不太具体,即 List
。
在这种情况下,对于编译器来说,分配给 moons
变量的实例具有什么特定类型并不重要 - 您正在询问的实现 - 只有变量类型很重要。
我希望这能回答你的问题,如果没有请问,我会尽力详细说明。
让我们看看你的方法声明:
private static ArrayList<HeavenlyBody> makingSetOfMoons(){
Java 期望方法 return 一个 ArrayList<HeavenlyBody>
但是你 return 声明的 moons 变量:
List<HeavenlyBody> moons = ....
分配给变量的列表子类型类型无关紧要——Java 将变量视为 List<HeavenlyBody>
,纯粹而简单。即使您将 ArrayList<HeavenlyBody>
分配给此变量,Java 编译器也无法使用它为您执行自动隐式转换,因为您稍后可以在方法中更改变量引用的对象,现在或更晚,这可能会导致问题。正如 Jon Skeet 所说 here:
The compiler uses the declared type of the variable to know how it can be used... it's as simple as that.
一个解决方案是将 moons 变量更改为:
ArrayList<HeavenlyBody> moons = ...
这可行但会出错,因为“编写接口代码”几乎总是更好(参见:What does it mean to “program to an interface”?)。最好在其声明中更改方法期望的 return 类型:
private static List<HeavenlyBody> makingSetOfMoons() {
这一切都归结为引用变量和引用(或对象)之间的区别,特别是两者的类型。这是一个很好的区别,但也是一个重要的区别。
变量及其类型:
List<HeavenlyBody> moons
这个变量是一个 reference 变量,它引用一个对象,而不是一个 primitive 变量,它引用一个原始类型。
当前您将此引用分配给变量:
new ArrayList<>();
所以变量的类型是 List<HeavenlyBody>
并且它引用的引用的类型是 ArrayList<HeavenlyBody>
您在理解中似乎遗漏了以下表达式的两个部分:
Type identifier = new SubType();
注意,identifier
是 类型 Type
的变量(并且 不直接 类型 SubType
),并且它可以存储对任何其他类型对象的引用,只要该“其他”类型是 Type
的子类型(包括 Type
本身,因为类型是Java).
中自身的子类型
因此:
private static ArrayList<HeavenlyBody> makingSetOfMoons() {
List<HeavenlyBody> moons = new ArrayList<>();
...
return moons;
}
不会编译,因为 moons
的类型是 List<HeavenlyBody>
。
问:为什么这样设计?
答:Because
in Java programming language, every variable and every expression has a type that is known at compile time.JSL - Chapter 4: Types, Values, and Variables
假设你有:
public ArrayList<SomeType> something(List<SomeType> list) {
...
return list;
}
你可以观察到,List<SomeType> list
可以接受 任何 的子类型;但是,在这种情况下,如果您被允许 return list
(List<SomeType>
类型),您很有可能会收到运行时异常,因为并非每个子List<E>
的类型可以转换为 ArrayList<E>
.
我知道我的代码内部发生了什么,也知道如何修复错误。但是,我不明白为什么事情会这样发生。具体来说,为什么我的方法 returns interface List 而不是 ArrayList 接口实现?
最让我困惑的是在将 moons
实现为 ArrayList
之后,它仍然是 returns 接口。
提前致谢!
private static ArrayList<HeavenlyBody> makingSetOfMoons(){
List<HeavenlyBody> moons = new ArrayList<>();
for(HeavenlyBody planet : Main.planets){
moons.addAll(planet.getSatellites());
}
return moons;
}
错误输出:
Incompatible types. Found: 'java.util.List<com.practice.HeavenlyBody>', required: 'java.util.ArrayList<com.practice.HeavenlyBody>'
编译器期望 return 类型 ArrayList
或更具体类型的对象(扩展 ArrayList
的 class )的方法。但是,从该方法中编辑的 moons
变量 return 的类型不太具体,即 List
。
在这种情况下,对于编译器来说,分配给 moons
变量的实例具有什么特定类型并不重要 - 您正在询问的实现 - 只有变量类型很重要。
我希望这能回答你的问题,如果没有请问,我会尽力详细说明。
让我们看看你的方法声明:
private static ArrayList<HeavenlyBody> makingSetOfMoons(){
Java 期望方法 return 一个 ArrayList<HeavenlyBody>
但是你 return 声明的 moons 变量:
List<HeavenlyBody> moons = ....
分配给变量的列表子类型类型无关紧要——Java 将变量视为 List<HeavenlyBody>
,纯粹而简单。即使您将 ArrayList<HeavenlyBody>
分配给此变量,Java 编译器也无法使用它为您执行自动隐式转换,因为您稍后可以在方法中更改变量引用的对象,现在或更晚,这可能会导致问题。正如 Jon Skeet 所说 here:
The compiler uses the declared type of the variable to know how it can be used... it's as simple as that.
一个解决方案是将 moons 变量更改为:
ArrayList<HeavenlyBody> moons = ...
这可行但会出错,因为“编写接口代码”几乎总是更好(参见:What does it mean to “program to an interface”?)。最好在其声明中更改方法期望的 return 类型:
private static List<HeavenlyBody> makingSetOfMoons() {
这一切都归结为引用变量和引用(或对象)之间的区别,特别是两者的类型。这是一个很好的区别,但也是一个重要的区别。
变量及其类型:
List<HeavenlyBody> moons
这个变量是一个 reference 变量,它引用一个对象,而不是一个 primitive 变量,它引用一个原始类型。
当前您将此引用分配给变量:
new ArrayList<>();
所以变量的类型是 List<HeavenlyBody>
并且它引用的引用的类型是 ArrayList<HeavenlyBody>
您在理解中似乎遗漏了以下表达式的两个部分:
Type identifier = new SubType();
注意,identifier
是 类型 Type
的变量(并且 不直接 类型 SubType
),并且它可以存储对任何其他类型对象的引用,只要该“其他”类型是 Type
的子类型(包括 Type
本身,因为类型是Java).
因此:
private static ArrayList<HeavenlyBody> makingSetOfMoons() {
List<HeavenlyBody> moons = new ArrayList<>();
...
return moons;
}
不会编译,因为 moons
的类型是 List<HeavenlyBody>
。
问:为什么这样设计?
答:Because
in Java programming language, every variable and every expression has a type that is known at compile time.JSL - Chapter 4: Types, Values, and Variables
假设你有:
public ArrayList<SomeType> something(List<SomeType> list) {
...
return list;
}
你可以观察到,List<SomeType> list
可以接受 任何 的子类型;但是,在这种情况下,如果您被允许 return list
(List<SomeType>
类型),您很有可能会收到运行时异常,因为并非每个子List<E>
的类型可以转换为 ArrayList<E>
.