在Java8中使用相同签名的两个默认方法实现两个接口

在Java8中使用相同签名的两个默认方法实现两个接口,java,interface,java-8,Java,Interface,Java 8,假设我有两个接口: public interface I1 { default String getGreeting() { return "Good Morning!"; } } public interface I2 { default String getGreeting() { return "Good Afternoon!"; } } 如果我想实现这两个,将使用什么实现 public class C1 implemen

假设我有两个接口:

public interface I1
{
    default String getGreeting() {
        return "Good Morning!";
    }
}

public interface I2
{
    default String getGreeting() {
        return "Good Afternoon!";
    }
}
如果我想实现这两个,将使用什么实现

public class C1 implements I1, I2
{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }
}

这是一个编译时错误。两个接口不能有两个实现

但是,如果在
C1
中实现
getGreeting
方法,则这是正确的:

public class C1 implements I1, I2 // this will compile, bacause we have overridden getGreeting()
{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }

    @Override public String getGreeting()
    {
        return "Good Evening!";
    }
}
我只想补充一点,即使I1中的方法是抽象的,而I2中是默认的,也不能同时实现这两种方法。这也是一个编译时错误:

public interface I1
{
    String getGreeting();
}

public interface I2
{
    default String getGreeting() {
        return "Good afternoon!";
    }
}

public class C1 implements I1, I2 // won't compile
{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }
}

这并不是这个问题所特有的。但是,我仍然认为它为上下文增加了一些价值。作为@toni77答案的补充,我想补充一点,默认方法可以从实现类调用,如下所示。在下面的代码中,从被重写的方法调用接口I1中的默认方法
getGreeting()

public interface I1 {
     default String getGreeting() {
        return "Good Morning!";
     }
}

public interface I2 {
    default String getGreeting() {
        return "Good Night!";
    }
}

public class C1 implements I1, I2 {       
    @Override
    public String getGreeting() {
        return I1.super.getGreeting();
    }
}

如果一个类实现了两个接口,这两个接口都有一个具有相同签名的java-8默认方法(如您的示例中所示),则实现类为。使用
I1.super.getGreeting()的类。它可以访问其中一个、两个或两个。所以下面是C1的一个有效实现

public class C1 implements I1, I2{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }

    @Override //class is obliged to override this method
    public String getGreeting() {
        //can use both default methods
        return I1.super.getGreeting()+I2.super.getGreeting();
    }

    public String useOne() {
        //can use the default method within annother method
        return "One "+I1.super.getGreeting();
    }

    public String useTheOther() {
        //can use the default method within annother method
        return "Two "+I2.super.getGreeting();
    }


}

有一种情况下,这实际上是根据处置规则工作的。如果其中一个接口扩展了另一个接口

使用上面的示例:

public interface I2 extends I1 {
    default String getGreeting() {
        return "Good Afternoon!";
    }
}
结果将是:

下午好

然而,我相信这将是一个大问题。默认接口的全部原因是允许库开发人员在不破坏实现者的情况下开发API

可以理解的是,它们不允许在没有继承结构的情况下通过扩展编译方法,因为库开发人员可能会劫持行为

然而,这有可能弄巧成拙。如果一个类实现了两个与层次结构视图无关的接口,但它们都定义了相同的默认方法签名,那么扩展这两个接口的类将不会编译。(如上所示)


可以想象,两个不同的库开发人员可以决定在不同的时间使用公共签名添加默认方法;事实上,这很可能发生在实现类似概念的库中,例如数学库。如果您碰巧是在同一个类中实现两个接口的可怜虫,那么在更新时您将被破坏

我相信规则是实现重复默认方法的类“必须”重写实现。。以下代码编译并运行良好

public class DupeDefaultInterfaceMethods {

interface FirstAbility {
    public default boolean doSomething() {
        return true;
    }
}

interface SecondAbility {
    public default boolean doSomething() {
        return true;
    }
}

class Dupe implements FirstAbility, SecondAbility {
    @Override
    public boolean doSomething() {
        return false;
    }
}

public static void main(String[] args) {
    DupeDefaultInterfaceMethods ddif = new DupeDefaultInterfaceMethods();
    Dupe dupe = ddif.new Dupe();
    System.out.println(dupe.doSomething());

    }
}


Java8中的默认方法可以看作是多重继承的一种形式(属性不能被继承的情况除外)

默认方法背后的主要动机是,如果在某个时刻我们需要向现有接口添加方法,我们可以在不更改现有实现类的情况下添加方法。这样,接口仍然与旧版本兼容。这是一个很酷的功能。然而,我们应该记住使用默认方法的动机,并且应该保持接口和实现的分离

interface First{ 
    // default method 
    default void show(){ 
         System.out.println("Default method implementation of First interface."); 
}  } 

interface Second{ 
   // Default method 
   default void show(){ 
        System.out.println("Default method implementation of Second interface."); 
}  } 

// Implementation class code 
public class Example implements First, Second{ 
    // Overriding default show method 
    public void show(){
       First.super.show();
       Second.super.show();
} 
    public static void main(String args[]){ 
       Example e = new Example(); 
       e.show(); 
}  }

如果
I2
扩展了
I1
并且
C1
实现了
I2
!!?我认为这将编译…还是我错了?谢谢你的问题!我刚刚试过:是的,不管I1中的getGreeting()是抽象的还是默认的,它都会编译。如果I1中的方法是默认的,这意味着您可以覆盖Ritance中接口中的默认实现…是的,确切地说…它将是继承的方法。这里有一篇关于默认方法的文章:请注意,如果I2没有具有重复签名的默认方法,这将编译并运行(当然)。如果随后在编译I1和C1之后将该默认方法添加到I2,那么C1(之前运行良好)将抛出运行时错误。对于向现有接口添加默认方法是“安全的”这一观点,这是一个很大的警告——在某些情况下,这样做可能会破坏现有代码。如果您正在维护库,这一点非常重要。我支持@James_D关于这可能会破坏现有代码的观点。这似乎是一个必须解决的问题。
interface First{ 
    // default method 
    default void show(){ 
         System.out.println("Default method implementation of First interface."); 
}  } 

interface Second{ 
   // Default method 
   default void show(){ 
        System.out.println("Default method implementation of Second interface."); 
}  } 

// Implementation class code 
public class Example implements First, Second{ 
    // Overriding default show method 
    public void show(){
       First.super.show();
       Second.super.show();
} 
    public static void main(String args[]){ 
       Example e = new Example(); 
       e.show(); 
}  }