2017-08-09 137 views
12

我有以下情況:類的靜態方法具有相同簽名的接口默認方法

class C { 
    static void m1() {} 
} 

interface I { 
    default void m1() {} 
} 

//this will give compilation error : inherited method from C cannot hide public abstract method in I 
class Main extends C implements I { 

} 

下面是我的問題:

  1. 我知道實例方法將覆蓋默認的方法但是如果類中的靜態方法與Interface中的缺省方法具有相同的簽名呢?

  2. 如果靜態方法m1()class C將公共那麼編譯錯誤將是:

    靜態方法M1()的抽象方法在一

所以當訪問修飾符衝突默認情況下,它試圖隱藏,當它公開時,它是衝突的。爲什麼這種差異?它背後的概念是什麼?

+0

你正在使用哪種編譯器? – Hulk

+1

這應該不依賴於默認實現或訪問級別的存在 - 在我看來,編譯錯誤應該是相同的。在eclipse中,它始終是「這個靜態方法不能從I中隱藏實例方法」,在[path]中使用oracle jdk 1.8.0_121「[path] /A.java:[5,17] m1()」.A不能實現m1()in [path] .I 重寫方法是靜態的 「 – Hulk

+0

我正在使用1。8,它顯示不同的編譯錯誤 – user2185089

回答

0

因爲在Java類方法也可以使用實例變量調用,這個結構會導致歧義:

Main m = new Main(); 

m.m1(); 

目前還不清楚最後一條語句應該調用類方法C.m1()或實例方法I.m1()

1

回答你的第一個問題:

無論是「類靜態方法」和「接口默認方法」是可用於該類Main,因此,如果他們有相同的簽名,它會創建歧義。

例如:

class C{ 
    static void m1(){System.out.println("m1 from C");} 
} 

public class Main extends C{ 
    public static void main(String[] args) { 
     Main main=new Main(); 
     main.m1(); 
    } 
} 

輸出:m1 from C

同樣,

interface I{ 
    default void m1(){System.out.println("m1 from I");} 
} 

public class Main implements I{ 
    public static void main(String[] args) { 
     Main main=new Main(); 
     main.m1(); 
    } 
} 

輸出:m1 from I

正如你可以看到,這兩個可類似地訪問。因此,這也是衝突,當你實現我和擴展C.

回答你的第二個問題的原因:

如果你的歸類和接口都在同一個包,默認和公共訪問修飾符應該工作類似。

此外,m1()C是靜態的,不能被覆蓋,因此它不能被視爲I實施m1()等編制問題。

希望有幫助!

+0

類和接口在同一個包中,仍然行爲不同 – user2185089

+0

你能告訴我整個代碼嗎? – OutOfMind

+0

如果有什麼比您提到的更多 – OutOfMind

1

我會回答,因爲第二個已經回答

我知道實例方法將覆蓋默認的方法,但 你的第一個問題是什麼,如果在類的靜態方法在接口相同的簽名作爲默認的方法 ?

我假設您使用的是JDK 1.8,因此會產生混淆。 default接口方法中的修飾符不是在談論其訪問規範。相反,它提到接口本身需要實現這種方法。該方法的訪問規範仍然是公開的。從JDK8開始,接口允許您使用默認修飾符指定方法,以允許以向後兼容的方式擴展接口。

在您的界面中,您必須給default void m1() {}編譯才能成功。通常我們只是在一個接口中以抽象的方式定義它們,如void m1();您必須實現該方法,因爲您已將該方法指定爲默認方法。希望你能理解。

+0

*「從JDK8開始,接口允許您使用默認修改器指定方法」* - 這是關於什麼?接口中的方法總是「公開」的(即使沒有給出訪問修飾符) – Marco13

+0

如果您完全閱讀答案,您將理解我想傳達的內容。如果使用默認修飾符定義方法,編譯器將強制您在接口本身中實現該方法。因爲這是新的,我以爲OP錯過了這個功能。 – Fayaz

+0

如果你覺得我沒有把它做得正確,或者你可以做得更好,那就隨意做吧 – Fayaz

4

最終這歸結爲一個事實,當你有這樣的事情:

class Me { 
    public static void go() { 
     System.out.println("going"); 
    } 
} 

這些都將被允許:

Me.go(); 
Me meAgain = new Me(); 
meAgain.go(); // with a warning here 

野趣的是,這將工作太例如:

Me meAgain = null; 
meAgain.go(); 

就我個人而言,我仍然認爲這是設計缺陷,無法收回du e兼容性 - 但我希望編譯器不會允許我從實例訪問靜態方法。

你的第一個問題是不相關的Java-8每本身,它一直是這樣的java-8前:

interface ITest { 
    public void go(); 
} 

class Test implements ITest { 
    public static void go() { // fails to compile 

    } 
} 

默認方法只是遵循相同的規則在這裏。爲什麼發生這種情況實際上在堆棧溢出中有相當多的細節 - 但潛在的想法是潛在的這會引起混淆調用哪種方法(想象ITest將是Test將會擴展的類,並且你做ITest test = new Test(); test.go();方法是,你哪位?)

我認爲,出於同樣的原因,這是不允許也(這基本上是你的第二個問題,否則你就必須使用相同的特徵碼的靜態和非靜態方法)

static class Me { 
    static void go() { 

    } 

    void go() { 

    } 
} 

有趣的是,這是一種固定的(我猜他們意識到它會成爲現實lly不好再做同樣的錯誤)在方法參考中:

static class Mapper { 
    static int increment(int x) { 
     return x + 1; 
    } 

    int decrement(int x) { 
     return x - 1; 
    } 
} 


Mapper m = new Mapper(); 
IntStream.of(1, 2, 3).map(m::increment); // will not compile 
IntStream.of(1, 2, 3).map(m::decrement); // will compile 
相關問題