我遇到了这个问题:
以下是什么输出?
1 public class A {
2 public static void main(String[] args){
3 I i = new I() {};
4 System.out.println(I.x + i.getValue() + "" + i);
5 }
6 }
7
8 interface I {
9 int x = 10;
10
11 public default int getValue() {
12 return 5;
13 }
14
15 public default String toString() {
16 return "I";
17 }
18 }
我的想法:
我的第一直觉告诉我 - 我是新的我(){} ?因此我们无法实例化接口 - 问题1.
然后我认为公共默认字符串toString()?重写Object类方法?听起来不错 - 问题2
可能的答案:
a)10I
b)15I
c)由于第11行而导致编译失败
d)编译因第15行而失败
e)由于多个错误导致编译失败
解释了我的想法后,我选择了答案E)这是错误的。正确的答案是D)我也是对的。
我的问题 - 为什么以下声明有效?
I i = new I() {};
由于" {}"这句话是否做了我不理解的事情。添加到它?据我所知,new
关键字表示:实例化。
答案 0 :(得分:8)
声明没有错:
I i = new I() {};
它只是实例化一个实现I
接口的匿名类。由于I
接口只有默认方法,因此如果没有toString()
方法的问题,则空体就足以实现它。
JLS 9.4.1.2表示接口不能具有toString()
方法的默认实现:
如果默认方法与类Object 的非私有方法等效,则是编译时错误,因为实现该接口的任何类都将继承自己的方法实现。
禁止将一个Object方法声明为默认方法可能会令人惊讶。毕竟,有像java.util.List这样的情况,其中精确定义了toString和equals的行为。然而,当理解一些更广泛的设计决策时,动机会变得更加清晰:
- 首先,允许从超类继承的方法覆盖从超级接口继承的方法(第8.4.8.1节)。因此,每个实现类都会自动覆盖接口的toString默认值。这是Java编程语言中的长期行为。我们希望通过默认方法的设计来改变它,因为这会与允许接口不引人注意地发展的目标相冲突,只有当类没有通过类层次结构时才提供默认行为。
- 其次,接口不从Object继承,而是隐式声明许多与Object(第9.2节)相同的方法。因此,在Object中声明的toString和在接口中声明的toString没有共同的祖先。充其量,如果两者都是班级继承的候选人,那么他们就会发生冲突。解决这个问题需要对类和接口继承树进行笨拙的混合。
- 第三,在接口中声明Object方法的用例通常假定为线性接口层次结构;该功能并不能很好地概括为多个继承方案。
- 第四,Object方法非常基础,允许任意超级接口静默添加一个改变其行为的默认方法似乎很危险。
toString()
是Object
类的方法,因此在任何接口中都不能有默认实现。
答案 1 :(得分:2)
由于添加了“{}”,我不明白?
您正在实例化匿名类
I i = new I() {};
答案 2 :(得分:2)
I i = new I(){};创建一个实现接口I的匿名类。由于接口I没有任何抽象方法可以实现,括号{}内的匿名类的主体可以为空。
答案 3 :(得分:2)
默认方法无法覆盖java.lang.Object
的方法,例如toString()
。