2014-06-18 127 views
94

保護靜態的我所經歷的這個問題Is there a way to override class variables in Java? 有36個upvotes第一個評論是:爲什麼我們不應該在Java中使用

如果你看到一個protected static,運行。

任何人都可以解釋爲什麼protected static皺起了眉頭?

+5

受保護的靜態字段沒有任何問題,只要它是「final」即可。跨類共享的可變靜態字段絕對是令人擔憂的。更新靜態字段的多個類不可能是可靠或易於遵循的,特別是因爲任何受保護的字段或方法的存在意味着該類意味着被其他包中的類擴展,可能不受該類控制的類包含受保護字段的類的作者。 – VGR

+5

@VGR,'final'並不意味着該字段是不可變的。你總是可以修改'final'引用變量引用的'object'。 – Zeeshan

+0

@VGR我不同意。您創建靜態變量的唯一原因是隻能通過繼承來從另一個包中訪問它,並且訪問單個字段不應該是繼承的原因。這是一個有缺陷的設計,海事組織,如果你訴諸於此,你應該重新考慮你的應用程序的結構。這只是我的看法。 –

回答

62

它比直接問題更具風格。這表明你沒有正確地思考班上發生的事情。

想想static意味着:

這個變量存在於一流水平,它不會單獨爲每個實例存在,它沒有在延長我類是獨立存在的。

想想protected意味着:

此變量可以由這個類可以看出,類在同一個包和類延伸我

這兩個含義並不完全相互排斥,但它非常接近。

我可以看到你可能在一起使用這兩者的唯一情況是,如果你有一個被設計用於擴展的抽象類,然後擴展類可以使用原來定義的常量修改行爲。儘管如此,這仍然是一個非常微弱的理由,因爲你仍然可以更好地把常數作爲公開的。這使得一切都變得更加清晰,並且允許人們分類更靈活。

擴大和解釋第一點 - 嘗試這個例子代碼:

public class Program { 
    public static void main (String[] args) throws java.lang.Exception { 
     System.out.println(new Test2().getTest()); 
     Test.test = "changed"; 
     System.out.println(new Test2().getTest()); 
    } 
} 

abstract class Test { 
    protected static String test = "test"; 
} 

class Test2 extends Test { 
    public String getTest() { 
     return test; 
    } 
} 

您將看到的結果:

test 
changed 

自己的嘗試:https://ideone.com/KM8u8O

Test2能夠從Test訪問靜態成員test而不需要限定名稱 - 但它不會t繼承或獲得自己的副本。它看着完全相同的變量。

+2

你們都被遺忘了。這種情況的典型案例是有人希望在不公開的情況下訪問包(例如單元測試)。 – spudone

+3

@spudone,但單元測試通常放在同一個包中。爲了讓他們訪問你只需使用默認(包)訪問級別。受保護的訪問也允許訪問子類,這對單元測試不是必需的或相關的。 –

+0

雖然它回答正確的問題,我不親自得到這行代碼:'Test.test =「改變」;'。 'class Program'如何能夠訪問* protected *屬性'test'?不應* * protected *屬性只能從* Test *類的子項訪問?或者是因爲* protected *可以從*「在同一個包中的類」*中看到,如上面引用的那樣? (對不起,如果問題很蠢,我幾乎不知道Java) – Kamafeather

6

靜態成員不會被繼承,並且受保護的成員只對子類(當然是包含類)可見,所以protected staticstatic具有相同的可見性,這表明編碼人員會產生誤解。

+1

您能否提供您的第一個陳述來源?在一個快速測試中,受保護的靜態int被繼承並重用於一個沒有問題的子類。 – hiergiltdiestfu

+0

受保護的static與package-private static具有相同的可見性,而非private static。爲了增加這一點,如果你使用的是受保護的和靜態的,那麼最好只刪除訪問修飾符以使它成爲私有包(如果你的意圖是使它在包內是可訪問的) –

+2

嗯......不。包私有和'protected'不一樣。如果你只是說'靜態',這個字段只對同一個包中的子類可見。 –

10

我沒有看到爲什麼應該皺起眉頭的特殊原因。總是可以有替代方案來實現相同的行爲,並且它將取決於實際的架構,這些替代方案是否比受保護的靜態方法「更好」。但是一個例子,其中一個受保護的靜態方法是合理的,至少,可能是以下幾點:

(編輯拆分成獨立的程序包,讓使用protected更清晰)

package a; 
import java.util.List; 

public abstract class BaseClass 
{ 
    public Integer compute(List<Integer> list) 
    { 
     return computeDefaultA(list)+computeDefaultB(list); 
    } 

    protected static Integer computeDefaultA(List<Integer> list) 
    { 
     return 12; 
    } 
    protected static Integer computeDefaultB(List<Integer> list) 
    { 
     return 34; 
    } 
} 

從派生:

package a.b; 

import java.util.List; 

import a.BaseClass; 

abstract class ExtendingClassA extends BaseClass 
{ 
    @Override 
    public Integer compute(List<Integer> list) 
    { 
     return computeDefaultA(list)+computeOwnB(list); 
    } 

    private static Integer computeOwnB(List<Integer> list) 
    { 
     return 56; 
    } 
} 

另一派生類:

package a.b; 

import java.util.List; 

import a.BaseClass; 

abstract class ExtendingClassB extends BaseClass 
{ 
    @Override 
    public Integer compute(List<Integer> list) 
    { 
     return computeOwnA(list)+computeDefaultB(list); 
    } 

    private static Integer computeOwnA(List<Integer> list) 
    { 
     return 78; 
    } 
} 

protected static修改肯定可以在這裏有道理:

  • 的方法可以是static,因爲它們不依賴於實例變量。它們不是直接用作多態方法,而是作爲更復雜計算的一部分提供缺省實現的「實用」方法,並作爲實際實現的「構建塊」。
  • 該方法不應該是public,因爲它們是實現細節。它們不能是private,因爲它們應該由擴展類調用。他們也不能具有「默認」可見性,因爲那樣他們將無法訪問其他包中的擴展類。

(編輯:人們可以假設原文評論僅提及領域,而不是方法 - 那麼,但是,它太籠統)

+0

在這種情況下,您應該將默認實現定義爲'protected final'(因爲您不希望它們被覆蓋),而不是'static'。事實上,一個方法不使用實例變量並不意味着它應該是'靜態'(儘管它*可以*)。 –

+2

@Thomas Sure,在這種情況下,這也是可能的。一般來說:這當然是部分主觀的,但我的經驗法則是:當一個方法不打算多態使用,並且它可以是靜態的,那麼我將它設爲靜態。與使其成爲「final」不同,它不僅表明該方法不打算被重寫,還另外*使讀者清楚該方法不使用實例變量。所以簡潔地說:沒有理由不*使它變成靜態的。 – Marco13

+1

*當一個方法不打算多態使用時,它可以是靜態的,那麼我將它設爲靜態。* - 當你開始使用模擬框架進行單元測試時,這會讓你陷入麻煩。但是,這導致我們到一個不同的話題... –

3

保護的使用,以便它可以用於子類。在具體類的上下文中使用時定義受保護的靜態方法沒有任何邏輯,因爲您可以通過靜態方式訪問相同的變量。但是,編譯器將以靜態方式發出警告來訪問超類靜態變量。

23

這是因爲它是矛盾的,因此而皺起了眉頭。

製作一個可變protected意味着它將包內使用或這將是一個子類內繼承。

使變量static成爲類的成員,消除了繼承它的意圖。這隻留下了在包中使用的意圖,並且我們有package-private(沒有修飾符)。

我唯一可以發現這個有用的情況是,如果你聲明瞭一個應該用來啓動應用程序的類(比如JavaFX的Application#launch,並且只希望能夠從子類啓動,那麼確保該方法也是final禁止hiding但這不是「規範」,可能是爲了防止通過添加新的方式來啓動應用程序來增加更多的複雜性。這個:The Java Tutorials - Controlling Access to Members of a Class

+4

我不明白'static'會如何消除繼承它的意圖。因爲我的另一個包中的子類仍然需要超級的字段被「保護」來訪問,即使它是「靜態的」。 'package-private'不能幫助 –

+0

@AoboYang你說得對,這就是爲什麼有些人使用'protected static'。但它是一種代碼味道,因此是「* run *」部分。訪問修飾符和繼承是兩個不同的主題。是的,如果它是'package-private',你將無法訪問超類中的靜態成員。但是你不應該依賴繼承來引用'static'字段;這是設計不佳的標誌。你會注意到覆蓋'static'方法的嘗試沒有給出結果,這是一個清楚的跡象,表明繼承不是基於類的。如果你需要在類或包之外訪問,它應該是'public' –

+3

我有一個類首先帶有一些'private static' util函數,但我認爲有人可能想要從我的類中進行改進或定製,並且這些util函數也可能爲他們提供便利。 'public'可能不適合,因爲util方法不適用於我的類的實例的用戶。你能幫我弄清楚一個好的設計,而不是'受保護的'嗎?謝謝 –

3

其實protected static沒有什麼根本錯誤。如果你真的想要一個靜態變量或方法,可以看到包和聲明類的所有子類,那麼繼續前進並使之成爲protected static

有些人通常避免使用protected由於種種原因,有些人認爲非最終static變量應該通過各種手段來避免(我個人後者同情在一定程度上),所以我想的protectedstatic組合必須看到壞^ 2屬於這兩個組。

0

protected static沒有什麼錯。很多人忽略了一件事情,那就是你可能想爲靜態方法編寫測試用例,而這些靜態方法在正常情況下你不想公開。我注意到這對於在實用類中編寫靜態方法的測試特別有用。

相關問題