簡體   English   中英

使用相同的方法簽名但不同的返回類型實現兩個接口

[英]Implement two interfaces with the same method signature but different return type

可以說我有兩個接口:

public interface InterfaceA {
 public int getStuff();

}

public interface InterfaceB {
 public String getStuff();

}

現在我有一個實現這兩個接口的類:

public class Testing implements InterfaceA, InterfaceB{
 public String getStuff() {
   return "one";
 }

}

我的問題是,這會編譯嗎? 如果它編譯,它會運行嗎?

編輯:

我確實在我的計算機上嘗試了這個並且發現它已編譯並且確實運行了,但是當調用getStuff()時,程序終止於

未解決的編譯問題

任何想法Java在這里做的更低層次?

你不能這樣做,它不會編譯。

直接來自doc

interface Fish       { int    getNumberOfScales(); }
interface StringBass { double getNumberOfScales(); }
class Bass implements Fish, StringBass {
    // This declaration cannot be correct,
    // no matter what type is used.
    public ?? getNumberOfScales() { return 91; }
}

不可能聲明一個名為getNumberOfScales的方法,其簽名和返回類型與接口Fish和接口StringBass中聲明的方法兼容,因為類不能有多個具有相同簽名和不同原始返回類型的方法(§8.4) )。 因此,單個類不可能同時實現接口Fish和接口StringBass

因此你不能這樣做,它不會編譯。 在您的情況下,它會抱怨該方法試圖使用不兼容的類型,因為兩個方法具有相同的名稱但返回類型不同。

這會編譯嗎? 如果它編譯,它會運行嗎?

您可能會感到驚訝:它不會編譯,但如果確實如此,它將會運行。

Java編譯器要求您實現在類中聲明的接口中聲明的所有方法。 在你的情況,你必須實現 public int getStuff(); public String getStuff(); 如果不這樣做,編譯器將顯示錯誤:

測試不是抽象的,不會覆蓋接口中的抽象方法getStuff(),測試中的getStuff()無法在InterfaceA中實現getStuff()

現在,對於Java虛擬機,在您的類中使用這兩種方法是完全可以的。 但是,Java編譯器不允許它:

方法getStuff()已經在類Testing中定義

所以你的代碼不會以某種方式編譯。


任何想法Java在這里做的更低層次?

讓我們回到JVM本身並做一些邪惡的事情。 看看這個jasmin程序:

.class public Testing
.super java/lang/Object
.implements InterfaceA
.implements InterfaceB

.method public static getStuff()I
  ldc 666
  ireturn
.end method

.method public static getStuff()Ljava/lang/String;
  ldc "Evil stuff"
  areturn
.end method

.method public static main([Ljava/lang/String;)V
  .limit stack 10
  getstatic java/lang/System/out Ljava/io/PrintStream;
  dup
  invokestatic Testing/getStuff()I
  invokevirtual java/io/PrintStream/println(I)V
  invokestatic Testing/getStuff()Ljava/lang/String;
  invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
  return
.end method

我們聲明了一個類Testing,它聲明了兩個具有相同名稱和參數的方法,唯一的區別是返回類型。 它實現了兩個接口,並有一個public static void main(String[] args)方法,它調用兩個方法( getStuff()getStuff() )並顯示結果。 而這個程序實際上是有效的,虛擬機不會抱怨任何事情。

它為什么有效? JVM規范定義了這樣的方法的描述符

MethodDescriptor:
    ( ParameterDescriptor* ) ReturnDescriptor

使用此描述符(除了類和方法名稱)來解析運行時的方法。 由於它包含ReturnDescriptor ,因此可以使用多個具有相同名稱的方法。

編譯器怎么樣? 編譯器不會使用其描述符來識別方法,而是使用其簽名 Java語言規范包含:

如果兩個方法具有相同的名稱和參數類型,則它們具有相同的簽名。

如果滿足以下所有條件,則兩個方法或構造函數聲明M和N具有相同的參數類型:

  • 它們具有相同數量的形式參數(可能為零)

  • 它們具有相同數量的類型參數(可能為零)

  • 設A1,...,An為M的類型參數,讓B1,...,Bn為N的類型參數。將N的類型中每次出現的Bi重命名為Ai后,相應類型變量的界限為同樣,M和N的形式參數類型是相同的。

因此,無法通過源代碼中的返回類型來區分方法,並且編譯器禁止聲明僅在返回類型方面不同的方法。

未解決的編譯問題

僅當您嘗試運行帶有編譯器錯誤的程序時才會發生此異常。

並且沒有編譯的原因,你還沒有完全實現所有的接口方法。 您只為InterfaceB方法而不是InterfaceA方法。

注意:如果您在IDE中沒有看到ant錯誤,請重新啟動或清理一次項目。

interface I1 { void f(); }
interface I2 { int f(int i); }
interface I3 { int f(); }
class C { public int f() { return 1; } }

class C2 implements I1, I2 {
  public void f() {}
  public int f(int i) { return 1; } // overloaded
}

class C3 extends C implements I2 {
  public int f(int i) { return 1; } // overloaded
}

class C4 extends C implements I3 {
  // Identical, no problem:
  public int f() { return 1; }
}

出現這種困難是因為覆蓋,實現和重載會令人不快地混合在一起,而重載方法只能通過返回類型來區分。

現在試試這個: - interface I4 extends I1, I3 {}

您將得到的錯誤: - 接口I3和I1不兼容; 兩者都定義了f(),但具有不同的返回類型

在要組合的不同接口中使用相同的方法名稱通常也會導致代碼的可讀性混淆。 努力避免它。

看到這個鏈接

“未解決的編譯問題”特別是Eclipse編譯器功能(ECJ)。 即使您的代碼中存在嚴重錯誤,此編譯器也可以生成已編譯的文件。 只要你不運行錯誤的代碼,你就沒事了。 在您的情況下,因為您在getStuff()方法中getStuff()問題,Eclipse會創建如下所示的方法:

public class Testing implements InterfaceA, InterfaceB {
    @Override
    public String getStuff() {
        throw new Error("Unresolved compilation problem: \n"+
         "\tThe return type is incompatible with InterfaceA.getStuff()\n";
    }
}

所以只要你不啟動這個方法,你就可以了。 一旦啟動它,就會拋出一個錯誤。

如果使用javac進行編譯,則根本不會創建.class文件。 回到你原來的問題,不,Java語言不允許這樣(Java字節碼允許)。

如果您嘗試調用根本不存在的InterfaceA方法(在Eclipse編譯之后)會發生什么,這也很有趣:

public class Testing implements InterfaceA, InterfaceB {
    @Override
    public String getStuff() { // compilation error here, but class is still generated
        return "one";
    }

    public static void main(String[] args) {
        System.out.println("Hello");
        InterfaceA a = new Testing();
        int i = a.getStuff();
        System.out.println(i);
    }
}

當你運行它時,你會看到:

Hello
Exception in thread "main" java.lang.AbstractMethodError: compileTest.Testing.getStuff()I
    at compileTest.Testing.main(Testing.java:12)

也就是說:當您嘗試調用缺少實現的抽象方法時,JVM會拋出AbstractMethodError 從JVM的角度來看, String getStuff()int getStuff()是兩種完全不同的方法,第二種方法在我們的類中不存在。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM