web-dev-qa-db-ja.com

なぜJavaでは静的メソッドを抽象化できないのか

問題はJavaです。なぜ抽象静的メソッドを定義できないのですか?例えば

abstract class foo {
    abstract void bar( ); // <-- this is ok
    abstract static void bar2(); //<-- this isn't why?
}
558
hhafez

「抽象」は「機能を実装しない」、「静的」は「オブジェクトインスタンスがなくても機能がある」という意味です。そしてそれは論理的な矛盾です。

536
Tomalak

悪い言語デザイン抽象メソッドを使用するためだけにインスタンスを作成するよりも、静的抽象メソッドを直接呼び出す方がはるかに効果的です。列挙型を拡張できないための回避策として抽象クラスを使用する場合は特にそうです。これはもう1つの悪い設計例です。次のリリースでそれらの制限が解決されることを願っています。

293
lxndr

静的メソッドをオーバーライドすることはできませんので、抽象化しても意味がありません。さらに、抽象クラスの静的メソッドはそのクラスに属し、オーバーライドクラスではないため、とにかく使用することはできません。

140
GaryF

メソッドへのabstractname__アノテーションは、そのメソッドがサブクラスでオーバーライドされなければならないことを示します。

Javaでは、staticname__メンバー(メソッドまたはフィールド)をサブクラスでオーバーライドすることはできません(これは必ずしも他のオブジェクト指向言語では当てはまりません。Smalltalkを参照)。staticname__メンバーは非表示にすることができます。これはオーバーライドとは根本的に異なります。

静的メンバーはサブクラスでオーバーライドできないため、abstractname__アノテーションをそれらに適用することはできません。

余談ですが、インスタンス継承と同じように、他の言語でも静的継承がサポートされています。構文の観点からは、これらの言語では通常、クラス名をステートメントに含める必要があります。たとえば、Javaでは、ClassAでコードを書いていると仮定すると、これらは同等のステートメントです(methodA()が静的メソッドであり、同じシグネチャを持つインスタンスメソッドがない場合)。

ClassA.methodA();

そして

methodA();

Smalltalkでは、クラス名はオプションではないため、構文は次のようになります(Smalltalkでは、「主語」と「動詞」を区切るために。を使用するのではなく、代わりにそれを状態修正終了文字として使用します)

ClassA methodA.

クラス名は常に必要なので、メソッドの正しい「バージョン」は常にクラス階層をたどることで判断できます。その価値のために、私は時々staticname__の継承を見逃しています、そして私が最初にそれを始めたときにJavaの静的な継承の欠如によって噛まれました。さらに、Smalltalkはアヒルタイプです(したがって、契約によるプログラムをサポートしていません)。したがって、クラスメンバにはabstractname__修飾子はありません。

67
Jared

私も同じ質問をした、これがここにある理由

Abstractクラスが言っているので、それは実装を与えず、サブクラスがそれを与えることを可能にしません

そのため、サブクラスはスーパークラスのメソッドをオーバーライドする必要があります。

ルール番号1 - 静的メソッドは上書きできません

静的メンバーとメソッドはコンパイル時の要素なので、オーバーライド(実行時多型)ではなく静的メソッドのオーバーロード(コンパイル時多型)が許可されるのはそのためです。

だから、彼らは抽象的にはできません。

abstract static<--- Java Universeでは許可されていません

14
anshulkatta

これはひどい言語設計であり、それがなぜ不可能なのかについての理由ではありません。

実際、これは_Javaでどのように行うことができるかに関する実装です。

public class Main {

        public static void main(String[] args) {
                // This is done once in your application, usually at startup
                Request.setRequest(new RequestImplementationOther());

                Request.doSomething();
        }

        public static final class RequestImplementationDefault extends Request {
                @Override
                void doSomethingImpl() {
                        System.out.println("I am doing something AAAAAA");
                }
        }

        public static final class RequestImplementaionOther extends Request {
                @Override
                void doSomethingImpl() {
                        System.out.println("I am doing something BBBBBB");
                }
        }

        // Static methods in here can be overriden
        public static abstract class Request {

                abstract void doSomethingImpl();

                // Static method
                public static void doSomething() {
                        getRequest().doSomethingImpl();
                }

                private static Request request;
                private static Request getRequest() {
                        // If setRequest is never called prior, it will default to a default implementation. Of course you could ignore that too. 
                        if ( request == null ) {
                                return request = new RequestImplementationDefault();
                        }
                        return request;
                }
                public static Request setRequest(Request r){
                        return request = r;
                }

        }
}

===========================================以下の古い例==============

GetRequestを探し、getRequestImpl ... setInstanceを呼び出して、呼び出しが行われる前に実装を変更することができます。

import Java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * @author Mo. Joseph
 * @date 16 mar 2012
 **/

public abstract class Core {


    // ---------------------------------------------------------------        
    private static Core singleton; 
    private static Core getInstance() {
        if ( singleton == null )
            setInstance( new Core.CoreDefaultImpl() );  // See bottom for CoreDefaultImpl

        return singleton;
    }    

    public static void setInstance(Core core) {
        Core.singleton = core;
    }
    // ---------------------------------------------------------------        



    // Static public method
    public static HttpServletRequest getRequest() {      
        return getInstance().getRequestImpl();
    }


    // A new implementation would override this one and call setInstance above with that implementation instance
    protected abstract HttpServletRequest getRequestImpl();




    // ============================ CLASSES =================================

    // ======================================================================
    // == Two example implementations, to alter getRequest() call behaviour 
    // == getInstance() have to be called in all static methods for this to work
    // == static method getRequest is altered through implementation of getRequestImpl
    // ======================================================================

    /** Static inner class CoreDefaultImpl */
    public static class CoreDefaultImpl extends Core { 
        protected HttpServletRequest getRequestImpl() {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        }
    }

     /** Static inner class CoreTestImpl : Alternative implementation */
    public static class CoreTestImpl extends Core { 
        protected HttpServletRequest getRequestImpl() {
            return new MockedRequest();
        }
    }       

}

以下のように使用されます。

static {
     Core.setSingleton(new Core.CoreDefaultImpl());

     // Or

     Core.setSingleton(new Core.CoreTestImpl());

     // Later in the application you might use

     Core.getRequest().doSomething(); 

}
11
momomo
  • 抽象メソッドは、サブクラスでオーバーライドできるようにだけ定義されています。ただし、静的メソッドはオーバーライドできません。したがって、抽象的で静的なメソッドを持つのはコンパイル時エラーです。

    それでは、次の質問は、静的メソッドをオーバーライドできない理由です。

  • これは、静的メソッドが特定のクラスに属し、そのインスタンスに属していないためです。静的メソッドをオーバーライドしようとすると、コンパイルエラーやランタイムエラーは発生しませんが、コンパイラはスーパークラスの静的メソッドを隠すだけです。

5
Rahul Saxena

ゴッドジオンの答えはすでにあるようですが、実際的な解決策は見当たりません。もちろん、これは本当の問題であり、この構文をJavaで除外する正当な理由はありません。元の質問にはこれが必要とされるかもしれない文脈が欠けているので、私は文脈と解決策の両方を提供します:

同一のクラスの集まりに静的メソッドがあるとします。これらのメソッドはクラス固有の静的メソッドを呼び出します。

class C1 {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    private static void doMoreWork(int k) {
        // code specific to class C1
    }
}
class C2 {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    private static void doMoreWork(int k) {
        // code specific to class C2
    }
}

C1C2doWork()メソッドは同一です。 C3C4などstatic abstractが許可されている場合は、次のようにして重複コードを排除します。

abstract class C {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }

    static abstract void doMoreWork(int k);
}

class C1 extends C {
    private static void doMoreWork(int k) {
        // code for class C1
    }
}

class C2 extends C {
    private static void doMoreWork(int k) {
        // code for class C2
    }
}

static abstractの組み合わせは許可されていないため、これはコンパイルできません。しかし、これはstatic class構文で回避できます。

abstract class C {
    void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    abstract void doMoreWork(int k);
}
class C1 {
    private static final C c = new  C(){  
        @Override void doMoreWork(int k) {
            System.out.println("code for C1");
        }
    };
    public static void doWork() {
        c.doWork();
    }
}
class C2 {
    private static final C c = new C() {
        @Override void doMoreWork(int k) {
            System.out.println("code for C2");
        }
    };
    public static void doWork() {
        c.doWork();
    }
}

この解決策では、重複する唯一のコードは

    public static void doWork() {
        c.doWork();
    }
3
mhp

定義上、静的メソッドはthisを知る必要はありません。したがって、仮想メソッドにすることはできません(thisを通じて利用可能な動的サブクラス情報に従ってオーバーロードされます)。代わりに、静的メソッドのオーバーロードは、コンパイル時に利用可能な情報にのみ基づいています(つまり、スーパークラスの静的メソッドを参照すると、スーパークラスメソッドを呼び出しますが、サブクラスメソッドは呼び出しません)。

これによると、抽象静的メソッドは、その参照を定義済みの本体で置き換えることがないため、まったく役に立ちません。

3

抽象メソッドは、静的メソッドがその機能に静的にバインドされている間に抽象化が行われるため、静的メソッドを持つことはできません。静的メソッドは、インスタンス変数に依存しない動作を意味します。静的メソッドはクラスではなくオブジェクトに属します。それらはPERMGENと呼ばれるメモリ領域に格納され、そこからすべてのオブジェクトと共有されます。抽象クラスのメソッドは、それらの機能に動的にバインドされます。

2
Sunil Kumar Jha

ParentChildの2つのクラスがあるとします。 Parentabstractです。宣言は次のとおりです。

abstract class Parent {
    abstract void run();
}

class Child extends Parent {
    void run() {}
}

これは、Parentのどのインスタンスもrun()の実行方法を指定しなければならないことを意味します。

ただし、Parentabstractではないとします。

class Parent {
    static void run() {}
}

これはParent.run()が静的メソッドを実行することを意味します。

abstractメソッドの定義は「宣言されているが実装されていないメソッド」であり、それ自体は何も返しません。

staticメソッドの定義は、「呼び出されたインスタンスに関係なく、同じパラメータに対して同じ値を返すメソッド」です。

abstractメソッドの戻り値はインスタンスが変化するにつれて変化します。 staticメソッドはそうではありません。 static abstractメソッドはほとんど戻り値が定数であるメソッドですが、何も返しません。これは論理的な矛盾です。

また、static abstractメソッドにはそれほど大きな理由はありません。

2
HyperNeutrino

静的メソッドは、クラスのインスタンスなしで呼び出すことができます。あなたの例では、あなたはfoo.bar2()を呼び出すことができますが、foo.bar()を呼び出すことはできません。以下のコードでうまくいくでしょう。

foo var = new ImplementsFoo();
var.bar();

静的メソッドを呼び出した場合、それは常に同じコードで実行されます。上記の例で、ImplementsFooでbar2を再定義しても、var.bar2()を呼び出すとfoo.bar2()が実行されます。

Bar2が実装されていない場合(これが抽象的な意味です)、実装なしでメソッドを呼び出すことができます。それはとても有害です。

1
Mnementh

抽象クラスはOOPSの概念であり、静的メンバーはOOPSの一部ではないためです。
これで、インターフェース内で静的な完全メソッドを宣言でき、インターフェース内でmainメソッドを宣言することでインターフェースを実行できるようになりました。

interface Demo 
{
  public static void main(String [] args) {
     System.out.println("I am from interface");
  }
}
1

私は、この質問に対する答えを、インターフェイスのメソッド(親クラスの抽象メソッドのように機能する)が静的にできない理由で発見したと思います。 これが完全な答えです(私のものではありません)

それらを呼び出すにはクラスを指定する必要があるため、基本的に静的メソッドはコンパイル時にバインドできます。これはインスタンスメソッドとは異なります。インスタンスメソッドでは、メソッドの呼び出し元の参照のクラスがコンパイル時に不明になる可能性があります(したがって、呼び出されるコードブロックは実行時にのみ決定できます)。

静的メソッドを呼び出す場合は、それが実装されているクラス、またはその直接のサブクラスをすでに知っています。定義すれば

abstract class Foo {
    abstract static void bar();
}

class Foo2 {
    @Override
    static void bar() {}
}

それで、どんなFoo.bar();呼び出しも明らかに違法であり、あなたはいつもFoo2.bar();を使うでしょう。

これを念頭に置いて、静的抽象メソッドの唯一の目的は、そのようなメソッドを実装するようにサブクラスを強制することです。あなたは最初これが非常に間違っていると思うかもしれませんが、もしあなたがジェネリック型パラメータ<E extends MySuperClass>を持っているなら、E.doSomething()をすることができることをインターフェースを通して保証するのはいいでしょう。型消去のため、総称はコンパイル時にのみ存在することに注意してください。

それで、それは役に立ちますか?はい、多分それが原因でJava 8がインターフェース内で静的メソッドを許可している理由です(ただしデフォルトの実装でのみ)。クラスのデフォルト実装で抽象メソッドを抽象化しないのはなぜですか?単にデフォルトの実装を持つ抽象メソッドが実際には具象メソッドだからです。

デフォルト実装のない抽象/インタフェース静的メソッドではないのはなぜですか?どうやら、Javaがどのコードブロックを実行する必要があるかを識別する方法が理由である(私の答えの最初の部分)。

1
Blueriver

抽象静的メソッドを持つという考えは、そのメソッドにその特定の抽象クラスを直接使用することはできないということですが、その静的メソッドを実装できるのは一次導関数だけですつかいます)。

このようにして、例えばsortableObject抽象クラスを作成したり、ソートオプションのパラメータを定義する(自動)抽象静的メソッドとのインターフェースさえ作成することができます。

public interface SortableObject {
    public [abstract] static String [] getSortableTypes();
    public String getSortableValueByType(String type);
}

これで、これらすべてのオブジェクトに対して同じである主な型によってソートできるソート可能なオブジェクトを定義できます。

public class MyDataObject implements SortableObject {
    final static String [] SORT_TYPES = {
        "Name","Date of Birth"
    }
    static long newDataIndex = 0L ;

    String fullName ;
    String sortableDate ;
    long dataIndex = -1L ;
    public MyDataObject(String name, int year, int month, int day) {
        if(name == null || name.length() == 0) throw new IllegalArgumentException("Null/empty name not allowed.");
        if(!validateDate(year,month,day)) throw new IllegalArgumentException("Date parameters do not compose a legal date.");
        this.fullName = name ;
        this.sortableDate = MyUtils.createSortableDate(year,month,day);
        this.dataIndex = MyDataObject.newDataIndex++ ;
    }
    public String toString() {
        return ""+this.dataIndex+". "this.fullName+" ("+this.sortableDate+")";
    }

    // override SortableObject 
    public static String [] getSortableTypes() { return SORT_TYPES ; }
    public String getSortableValueByType(String type) {
        int index = MyUtils.getStringArrayIndex(SORT_TYPES, type);
        switch(index) {
             case 0: return this.name ;
             case 1: return this.sortableDate ;
        }
        return toString(); // in the order they were created when compared
    }
}

今、あなたは作成することができます

public class SortableList<T extends SortableObject> 

型を取得し、ソートする型を選択するためのポップアップメニューを作成し、その型からデータを取得することでリストを並べ替えることができます。 SortableListのインスタンスは、静的メソッド "T"に直接アクセスできます。

String [] MenuItems = T.getSortableTypes();

インスタンスを使用しなければならないことに伴う問題は、SortableListがまだアイテムを持っていないかもしれませんが、すでに優先ソートを提供する必要があるということです。

Cheerio、オラフ。

0
Olaf Leimann

まず、抽象クラスに関する重要なポイント - 抽象クラスはインスタンス化できません( wiki を参照)。したがって、抽象クラスのanyインスタンスを作成することはできません。

現在、Javaが静的メソッドを処理する方法は、そのクラスのすべてのinstancesでメソッドを共有することです。

したがって、クラスをインスタンス化できない場合は、抽象メソッドが拡張されることを要求するため、そのクラスは抽象静的メソッドを持つことができません。

ブーム。

0
bruhhhhh

AbstractはAbstractメソッドに適用されるキーワードであるため、本文を指定しません。そして私達が静的キーワードについて話すならば、それはクラス領域に属します。

0
Ashish Ranjan

'abstract'はそのメソッドがオーバーライドされることを意味し、 'static'メソッドをオーバーライドすることはできません。

0
Praveen Kumar

Java doc によると、

静的メソッドは、オブジェクトではなく、それが定義されているクラスに関連付けられているメソッドです。クラスのすべてのインスタンスはその静的メソッドを共有します

Java 8では、デフォルトのメソッドとともに、静的メソッドもインターフェイスで使用できます。これにより、ライブラリ内でヘルパーメソッドを整理することが容易になります。インターフェイスに固有の静的メソッドを、別のクラスではなく同じインターフェイスに保持することができます。

この良い例は次のとおりです。

list.sort(ordering);

の代わりに

Collections.sort(list, ordering);

静的メソッドを使用する別の例は、 doc 自体にもあります。

public interface TimeClient {
    // ...
    static public ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default public ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }    
}
0
i_am_zero

通常のメソッドは、サブクラスによってオーバーライドされて機能を提供されることを意図している場合は抽象的になることがあります。クラスFooBar1, Bar2, Bar3などによって拡張されていると想像してみてください。したがって、それぞれが必要に応じてそれぞれ独自のバージョンの抽象クラスを持ちます。

現在では、静的メソッドは定義上クラスに属していますが、クラスのオブジェクトやそのサブクラスのオブジェクトとは関係ありません。それらは存在する必要すらありません、それらはクラスをインスタンス化することなく使用することができます。したがって、それらはすぐに使えるようにする必要があり、それらに機能を追加するためにサブクラスに依存することはできません。

0

Java 8のインターフェースでこれを行うことができます。

これはそれに関する公式の文書です:

https://docs.Oracle.com/javase/tutorial/Java/IandI/defaultmethods.html

0
Jim Showalter

あなたがクラスで静的メンバや静的変数を使用している場合、それはクラスのロード時にロードされるからです。

0
Manu

メソッドをstaticとして宣言すると、そのメソッドをそのクラス名で呼び出すことができます。そのクラスがabstractの場合も、本体が含まれていないため、呼び出すことは意味がありません。メソッドをstaticabstractの両方として宣言することはできません。

0
akash kumar