【问题标题】:Implementing two interfaces with two default methods of the same signature in Java 8在 Java 8 中实现两个具有相同签名的默认方法的两个接口
【发布时间】:2014-05-06 07:50:51
【问题描述】:

假设我有两个接口:

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());
    }
}

【问题讨论】:

  • 如果I2 扩展I1C1 实现I2 会怎样!!?我认为这会编译......还是我错了?
  • 感谢您的提问!我刚刚尝试过:是的,这将编译,无论 I1 中的 getGreeting() 是抽象的还是默认的。如果I1中的方法是默认的,这意味着你可以覆盖interface-inheritance中的默认实现...
  • 是的,完全是......它将被继承的方法。
  • 这是一篇关于默认方法的文章:Java 8 : Default method in Interface

标签: java interface java-8


【解决方案1】:

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

但是,如果你在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());
    }
}

【讨论】:

  • 请注意,如果 I2 没有带有重复签名的默认方法,这将编译并运行(当然)。如果您随后将该默认方法添加到 I2,则在编译 I1 和 C1 之后,C1(之前运行良好)将引发运行时错误。这是一个很大的警告,即向现有接口添加默认方法是“安全的”——在某些情况下这样做会破坏现有代码。如果您正在维护库,那么考虑这一点非常重要。
  • 我第二次@James_D 对此可能会破坏现有代码。这似乎是一个必须解决的问题。
【解决方案2】:

这不是特定于问题的。但是,我仍然认为它为上下文增加了一些价值。作为@toni77 答案的补充,我想补充一点,可以从实现类调用默认方法,如下所示。在下面的代码中,interface 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();
    }
}

【讨论】:

    【解决方案3】:

    如果一个类实现了 2 个接口,这两个接口都具有具有相同签名的 java-8 默认方法(如您的示例中所示),则实现类为 obliged to override the method。类can still access the default method 使用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();
        }
    
    
    }
    

    【讨论】:

      【解决方案4】:

      根据解析规则,这实际上是有效的。如果其中一个接口扩展了其他接口之一。

      使用上面的例子:

      public interface I2 extends I1 {
          default String getGreeting() {
              return "Good Afternoon!";
          }
      }
      

      结果是:

      下午好!

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

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

      但是,这有可能会弄巧成拙。如果一个类实现了两个与分层视图无关的接口,但都定义了相同的默认方法签名,则扩展这两个接口的类将无法编译。 (如上所示)

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

      【讨论】:

        【解决方案5】:

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

        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());
        
            }
        }
        

        > false
        

        【讨论】:

          【解决方案6】:

          Java 8 中的默认方法可以看作是多重继承的一种形式(除了属性不能被继承)。

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

          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(); 
          }  }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-12-13
            • 2022-01-22
            • 2011-07-26
            • 2018-05-31
            • 2018-11-04
            • 2015-03-11
            • 1970-01-01
            • 2015-10-19
            相关资源
            最近更新 更多