30

まず、erickson の「Java インターフェースで静的メソッドを定義できないのはなぜですか?」に対する有益な回答を読みました。. この質問は「なぜ」ではなく、「どうやって?」についてです。


編集:私の元の例は不適切でしたが、以下に残します。

ほとんどの場合、私がやりたいことはやり過ぎだと確信していますが、それが必要になるシナリオが 1 つあります。

もう一度ParametricFunction例を挙げます。ここで、ベッセル関数のような複雑な関数を考えてみましょう。ここではルックアップ テーブルが適切です。これは初期化する必要があるため、2 つのオプションはパラメーターをコンストラクターに直接渡すか、init(double[] parameters). getValue(double x)後者には、呼び出しごとに初期化をチェックするArrayIndexOutOfBoundsException必要がある (または初期化チェックと見なす必要がある)という欠点があるため、タイムクリティカルなアプリケーションでは、コンストラクター メソッドを使用することをお勧めします。

interface ParametricFunction {
  public double getValue(double x);
}

class BesselFunction implements ParametricFunction {
  public BesselFunction(double[] parameters) { ... }
  public double getValue(double x) { ... }
}

これは、別の問題、つまりインターフェースでのコンストラクターの不可能性に関係しています。そこで良い解決策は何でしょうか?もちろん、このinit(double[] parameters)アプローチを使用することもできますが、そうしない理由を述べました。
(編集:OK、ここではインターフェイスを実装する抽象クラスで十分です)

ParametricFunctionここで、が特定のパラメータ (正の整数など) のみを許可すると仮定します。コンストラクターに渡されたパラメーターの有効性を確認する方法は? -exceptionIllegalArgumentをスローすることも可能ですが、 acheckParametersValidity(double[] parameters)の方がはるかに便利です。ただし、パラメータのチェックは構築前に行う必要があるため、静的メソッドである必要があります。ParametricFunctionそして、インターフェイスを実装するすべてのクラスがこの静的メソッドを定義していることを確認する方法を知りたいのです。

私はこの例がかなり人為的であることinitを知っています.インターフェースを介してメソッドを単純に使用しない理由は議論の余地があります.私はまだ答えを知りたいです. 嫌ならアカデミックな問題と考えてください。

(元の例)

したがって、基本的には、1 つのインターフェイスで通常のメソッドとメソッドなどの両方を提供する必要がありgetSimilarObjectます。(構成された)例の場合

public interface ParametricFunction {
  /** @return f(x) using the parameters */
  static abstract public double getValue(double x, double[] parameters);

  /** @return The function's name */
  static abstract public String getName();

  /** @return Whether the parameters are valid  [added on edit] */
  static abstract public boolean checkParameters(double[] parameters);
}

その後

public class Parabola implements ParametricFunction {
  /** @return f(x) = parameters[0] * x² + parameters[1] * x + parameters[2] */
  static public double getValue(double x, double[] parameters) {
    return ( parameters[2] + x*(parameters[1] + x*parameters[0]));
  }
  static public String getName() { return "Parabola"; }
  // edit:
  static public boolean checkParameters(double[] parameters) {
    return (parameters.length==3);
  }
}

これは現在のJava標準では許可されていないため、これに最も近いものは何ですか?

この背後にあるアイデアは、いくつかParametricFunctionの をパッケージに入れ、Reflection を使用してそれらをすべてリストし、ユーザーがどれをプロットするかなどを選択できるようにすることです。明らかに、利用可能な の配列を含むローダー クラスを提供できますがParametricFunction、新しいものが実装されるたびに、そこに追加することも覚えておく必要があります。

編集:それを呼び出す例は

public double evaluate(String fnName, double x, double parameters) throws (a lot) {
  Class<ParametricFunction> c = (Class<ParametricFunction>) ClassLoader.getSystemClassLoader().loadClass(fnName);
  Method m = c.getMethod("getValue", x, parameters);
  return ((double) m.invoke(null));
}

と呼び出しevaluate("Parabola", 1, new double[]{1,2,0});ます。

4

9 に答える 9

22

インターフェイスを介して特定の静的メソッドを実装するようクラスに要求することはできません。Javaの用語では意味がありません。インターフェイスは、インターフェイスを実装するクラスに特定の非静的メソッドの存在を強制します。それが彼らの仕事です。

最も簡単な方法は、間違いなく、他のインスタンスを生成する何らかのファクトリ クラスを用意することです。はい、これは、新しいインスタンスを追加するときにそのファクトリを最新の状態に保つことを忘れないでください。その問題をすぐに取り上げます!

于 2010-04-22T10:23:01.570 に答える
4

Java 5 列挙型を試してみませんか? すなわち:

public enum ParametricFunctions implements ParametricFunction {
    Parabola() {
        /** @return f(x) = parameters[0] * x² + parameters[1] * x + parameters[2] */
        public double getValue(double x, double[] parameters) {
            return ( parameters[2] + x*(parameters[1] + x*parameters[0]));
        }

        public String getName() { return "Parabola"; }

        public boolean checkParameters(double[] parameters) {
            return (parameters.length==3);
        }
    },

    // other functions as enum members
}

これにより、静的関数型を簡単に検索でき、コンパイル時の安全性を維持しながら、インターフェイス型を別の場所で参照できます。列挙型にメソッドを配置して、名前で関数を検索できるようにすることもできます。


列挙型の方法でファイルサイズに関する懸念がある場合は編集してください。

その場合、各関数を独自のクラスとして定義できます。つまり、次のようになります。

public class Parabola implements ParametricFunction {

    /** @return f(x) = parameters[0] * x² + parameters[1] * x + parameters[2] */
    public double getValue(double x, double[] parameters) {
        return ( parameters[2] + x*(parameters[1] + x*parameters[0]));
    }

    public String getName() { return "Parabola"; }

    public boolean checkParameters(double[] parameters) {
        return (parameters.length==3);
    }

}

次に、多数の個別の実装ファイルを用意し、それらを 1 つの小さい列挙型のようなクラスに構成して、関数に静的にアクセスできるようにします。すなわち:

public class ParametricFunctions {  
    public static final ParametricFunction parabola = new Parabola(),
                                           bessel = new BesselFunction(),
                                           // etc
}

これにより、実装を分離したまま、単一の場所で関数を検索できます。名前検索用の静的コレクションに追加することもできます。別のコメントで述べたように、関数の読みやすさを維持できます。

import static ...ParametricFunctions.parabola;
// etc

public void someMethodCallingFit() {
    fit(parabola, xValues, yValues);
}
于 2010-04-22T10:15:18.050 に答える
3

この背後にある考え方は、いくつかのParametricFunctionをパッケージに入れ、Reflectionを使用してそれらすべてをリストし、ユーザーがプロットするものなどを選択できるようにすることです。

これは、より基本的な理由で失敗します。リフレクションでは、パッケージ内のすべてのクラスを一覧表示する方法がありません(クラスローダーメカニズムの柔軟性により、「パッケージ内のすべてのクラス」は明確に定義されたセットではないため)。

この種の最新のソリューションは、依存性注入フレームワークを介してアプリケーション構成の一部にすることです。

明らかに、利用可能なParametricFunctionの配列を含むローダークラスを提供することもできますが、新しいクラスを実装するたびに、そこに追加することも覚えておく必要があります。

さて、あなたのコンセプトでは、新しいものが実装されるたびに、それを同じパッケージに入れることを余儀なくされます。それを構成ファイルまたはローダークラス(実際には同じもの)に入れることで、その制限を取り除きます。

于 2010-04-22T08:47:54.967 に答える
3

あなた自身の質問に対するあなたの答えは、さらに単純化できます。ParametricFunctionインターフェースをそのままにして、以下を実装するシングルトンに変更しParabolaますParametricFunction

public class Parabola implements ParametricFunction {
  private static Parabola instance = new Parabola();

  private Parabola() {}

  static public ParametricFunction getInstance() {
    return instance;
  }

  public double getValue(double x, double[] parameters) {
    return ( parameters[2] + x*(parameters[1] + x*parameters[0]));
  }
  public String getName() { return "Parabola"; }
  public boolean checkParameters(double[] parameters) {
    return (parameters.length==3);
  }
}

実際、Parabola がシングルトン クラスである必要がある特別な理由がなければ、静的メソッドと属性を取り除き、コンストラクターを public にすることができます。

のインスタンスを作成する目的Parabolaは、アプリケーションを簡素化することです

以下の質問に答えて編集します。

標準の Java コンストラクトを使用して、特定のシグネチャを持つ静的メソッドをクラスに強制的に実装することはできません。Java には抽象静的メソッドのようなものはありません

ビルドの一部として実行され、ソース コードまたはコンパイル済みコードのいずれかをチェックする別のツールを作成することで、静的メソッドが実装されていることを確認できます。しかし、IMO、努力する価値はありません。getInstance()それを呼び出すコードをコンパイルする場合、または実行時にそれをリフレクティブに使用しようとすると、欠落が表示されます。私の意見では、それで十分なはずです。

その上、クラスをシングルトンにする必要がある説得力のある理由は思いつきません。つまり、なぜgetInstanceメソッドが必要なのか。

于 2010-04-22T09:47:33.890 に答える
1

その理由は読みやすさです: fit("Parabola", xValues, fValues) vs. fit(Parabola.getInstance(), xValues, fValues) vs. fit(new Parabola(), xValues, fValues)。内部データなしで引数によって完全に定義された関数のインスタンスが必要なのはなぜですか?

実際、指向オブジェクトプログラミングの基本について何かが欠けています...

オブジェクト Parabola を定義する場合、このオブジェクトは Parabola を表す必要があり、パラメーターをチェックするためのツールボックスではなく、OK など...

Parabola アイテムにはパラメーター (x、y ...) が含まれている必要があり、コンストラクターでそれらを渡すことができます...

double x;
double [] parameters;
public Parabola(double x, double[] parameters) {
  this.x = x;
  this.parameters = parameters;
}

したがって、パラメーターはクラスメンバー属性として宣言されているため、関数でパラメーターを使用しないでください...

public double getValue() {
  return ( this.parameters[2] + x*(this.parameters[1] + x*this.parameters[0]));
}

あとは電話するだけ

parabolaInstance.getValue();
于 2010-04-22T10:09:38.547 に答える
0

1 つの解決策 - すべてのメソッドを非静的にし、クラスにデフォルトのコンストラクターが必要であるという要件があります。その後、簡単にインスタンス化して、必要なメソッドを呼び出すことができます。

于 2010-04-22T09:17:43.850 に答える
0

@Sebastien: 両方のクラスがまったく同じ静的メソッド名を共有することに関心がないのはなぜですか? リフレクションを使用することは、メソッドが存在することを確認する唯一の方法かもしれません。getDescription() でクラスの説明を返したいと思います。異なるインスタンスで変更する必要があるのはなぜですか? そのため、このメソッドを静的でありながら、インターフェイスのような方法で実装することを強制したいと考えています。– トビアス・キエンツラー 3

すでに述べたように、メソッドを静的に宣言するということは、クラスから直接呼び出すことができ、クラス インスタンスを必要としないことを意味します。I.staticMethod() を呼び出す意味がないので (既に説明したように)、A.staticMethod1() と B.staticMethod2() を呼び出すことができます。A またはコンパイル時にわかるBクラス!

関連する ParametricFunction のインスタンスに関係なく getDescription が同じ説明を返すようにする場合は、ParametricFunction を抽象クラスにして、静的メソッドをこのクラスに直接実装します。次に、A、I、または B.getDescription(); を呼び出すことができます。(a、i、またはbでも...)。しかし、それを A と B に実装し、それを呼び出して A または B をスローした場合と同じままです...

インスタンスから静的メソッドを呼び出すことは良い方法ではなく、意味がないため、a.meth() または b.meth() ではなく、A.meth() または B.meth() を呼び出す必要があります。

A と B にその staticMethod を確実に実装してもらい、新しいクラスのインターフェイスを使用する他の誰かもそうするようにしたかったからです。– Tobias Kienzler 5 時間前

実際、「他の誰か」は通常、a.meth() または b.meth() を呼び出さないため、クラス C を作成して C.meth() を呼び出したい場合、C.meth() を呼び出すことはできません。 () は実装されていないか、静的ではありません...だから彼はそれを行います。そうしないと、 C.meth() が呼び出されないため、開発者に決して使用されない静的関数の実装を強制することも意味がありません...

何を追加したらいいのかわからない…

于 2010-04-22T16:43:30.253 に答える
0

やりたいことがうまくいかない…

インターフェイス I で静的メソッドを定義し、このインターフェイスのいくつかの実装 A と B を持ち、これらの静的メソッドの独自の実装をインターフェイス I で宣言したいとします。

I.staticMethod() を呼び出した場合、コンピューターは何をすべきかをどのように認識するか想像してみてください。AまたはBの実装を使用しますか?!!

インターフェイスでメソッドを宣言することの利点は、ポリモーフィズムを使用して、さまざまなオブジェクトの実装に対してこのメ​​ソッドを呼び出すことができるようにすることです...しかし、インスタンスからメソッドを呼び出さないため、静的メソッドの場合(実際にはできますが、実際にはそうではありません必要...)しかし、ClassName.xxxMethodでは、絶対に興味がありません...

したがって、これらの静的メソッドをインターフェイスに配置する必要はありません...両方の実装に配置し、 A.staticMethod() および B.staticMethod() で呼び出すだけです (また、同じメソッド名!)

静的メソッドをどのように呼び出したいのだろうか、表示するサンプル コードはありますか?

于 2010-04-22T09:28:43.357 に答える
0

インターフェイスのコンストラクター? え?Interface i = new Interface(double[] parameters) を呼び出せるようにしますか? そして、コンピューターは別の機会に自分で実装を選択するでしょうか? これは、インターフェイスの static と同じくらい奇妙です:D

あなたが言ったように、パラメーターのチェックは構築前に行う必要があります...しかし、これは、パラメーターが正常でない場合、構築時に例外を発生させることができないという意味ではありません。これは、構築されたオブジェクトが一貫していることを保証する、追加できる単なるセキュリティです。しかし、そのようなコードでは、以前の検証をバイパスすることはできません: 構築時に例外を発生させると、「バグがあります!」と通知されます。パラメータを検証しないと、「GUIを使用している誰かが間違った値を設定しようとしました。エラーメッセージを送信します...」というメッセージが表示されます。

実際には、値を検証する必要があり、オブジェクトは構築されていないため、モデル オブジェクトにこの検証を絶対に追加したいのはなぜですか? フォーム/Gui/あらゆる検証は、検証Javaクラスでどこでも実行できます...メソッドと検証実装を追加するParametricFunctionValidationHelperと呼ばれる別のクラスで、静的(またはそうでない)メソッドを設定するだけです。

public static boolean validateParametricFunction(String functionType, double[] parameters) {
  if ( functionType.equals("bessel") ) return validateBessel(parameters);
  if ( functionType.equals("parabola") ) return validateParabola(parameters);
}

functionType がどのように表されているかは問題ではありません (ユーザー インターフェイス、Web、または GUI から取得すると思われるため、String を選択します... Enum である可能性があります...

オブジェクトを構築した後で検証することもできます。

public static boolean validateParametricFunction(ParametricFunction pamFunc) {
  if ( pamFunc instanceOf BesselFunction ) return validateBessel(pamFunc.getParameters);
  ......
}

関数クラスに静的検証メソッドを配置することもできます。これにより、次のようになります。 public static boolean validateParametricFunction(ParametricFunction pamFunc) { if ( pamFunc instanceOf ParabolaFunction ) return ParabolaFunction.validateParabola(pamFunc.getParameters); }

はい、インターフェイスで静的メソッドを設定することはできませんが、とにかくそのようなメソッドをどのように呼び出すでしょうか?

のようなコードで

public static boolean validateParametricFunction(ParametricFunction pamFunc) {
  return ParametricFunction.validate(pamFunc);
}

??? インスタンスからではなくクラスから静的メソッドを呼び出すため、JVM は使用する静的メソッドの実装をまったく認識できないため、これは意味がありません。ValidateメソッドをParametricFunctionクラスに直接実装する場合にのみ意味がありますが、とにかくそのようなことを行う場合は、前にinstanceOfで示したのとまったく同じことを行う必要があります.pamFuncのインスタンス使用する必要がある検証の種類を選択する必要がある唯一の項目です...

そのため、非静的メソッドを使用して、次のようにインターフェイスに配置することをお勧めします。

public static boolean validateParametricFunction(ParametricFunction pamFunc) {
  return pamFunc.validate();
}

実際にすべきことは次のとおりです: - パラメータ (文字列?) を GUI / Web インターフェイス / から取得します - 適切な形式で文字列パラメータを解析します (文字列から int...) - 検証クラス (静的メソッドかどうか) でこれらのパラメータを検証します- 検証がない場合 -> ユーザーにメッセージを出力 - Else 構成オブジェクト - オブジェクトを使用

インターフェイスに静的メソッドが必要な場所はどこにもありません...

于 2010-04-26T15:17:41.337 に答える