インスタンス経由で静的メソッドを呼び出すことがJavaコンパイラのエラーにならないのはなぜですか?
質問
あなたが私が意味する動作を知っていると確信しています-次のようなコード:
Thread thread = new Thread();
int activeCount = thread.activeCount();
コンパイラの警告を引き起こします。なぜエラーではないのですか?
編集:
明確にするために、質問はスレッドとは関係ありません。スレッドの例は、実際に混乱させる可能性があるため、これについて議論するときにしばしば与えられることを理解しています。しかし、本当に問題なのは、そのような使用法は常にナンセンスであり、そのような呼び出しを(きちんと)書いてそれを意味することはできないということです。このタイプのメソッド呼び出しの例はどれも気難しいでしょう。ここに別のものがあります:
String hello = "hello";
String number123AsString = hello.valueOf(123);
各Stringインスタンスに" String valueOf(int i)"が含まれているように見えます。メソッド。
解決
基本的に、Javaデザイナーは言語の設計時にミスを犯したと思いますが、関連する互換性の問題のために修正するには遅すぎます。はい、それは非常に誤解を招くコードにつながる可能性があります。はい、あなたはそれを避けるべきです。はい、IDEをエラーIMOとして扱うように設定する必要があります。自分で言語を設計する必要がある場合は、回避すべき種類の例としてそれを念頭に置いてください:)
DJClayworthの主張に答えるために、C#で許可されていることを以下に示します。
public class Foo
{
public static void Bar()
{
}
}
public class Abc
{
public void Test()
{
// Static methods in the same class and base classes
// (and outer classes) are available, with no
// qualification
Def();
// Static methods in other classes are available via
// the class name
Foo.Bar();
Abc abc = new Abc();
// This would *not* be legal. It being legal has no benefit,
// and just allows misleading code
// abc.Def();
}
public static void Def()
{
}
}
なぜ誤解を招くと思うのですか?コード someVariable.SomeMethod()
を見ると、 someVariable
の値を使用することが期待されるためです。 SomeMethod()
が静的メソッドの場合、その期待は無効です。コードは私をだましています。どうしてそれが良いものになるのでしょうか?
Javaは、初期化されていない可能性のある変数を使用して静的メソッドを呼び出すことはできませんが、使用する情報は変数の宣言された型だけであるにもかかわらずです。それは一貫性がなく、役に立たない混乱です。許可する理由
EDIT:この編集は、静的メソッドの継承を許可するというクレイトンの回答に対する応答です。そうではありません。静的メソッドはポリモーフィックではありません。以下に、それを示す短いが完全なプログラムを示します。
class Base
{
static void foo()
{
System.out.println("Base.foo()");
}
}
class Derived extends Base
{
static void foo()
{
System.out.println("Derived.foo()");
}
}
public class Test
{
public static void main(String[] args)
{
Base b = new Derived();
b.foo(); // Prints "Base.foo()"
b = null;
b.foo(); // Still prints "Base.foo()"
}
}
ご覧のとおり、 b
の実行時の値は完全に無視されます。
他のヒント
エラーになるのはなぜですか?インスタンスはすべての静的メソッドにアクセスできます。静的メソッドは、インスタンスの状態を変更できません( にしようとすると、コンパイルエラーになります)。
よく知られている例の問題は、静的なメソッド呼び出しではなく、スレッドに固有のものです。 thread
によって参照されるスレッドの activeCount()
を取得しているように見えますが、実際には呼び出しスレッドのカウントを取得しています。これは、プログラマとしてのあなたが犯している論理エラーです。この場合、コンパイラーは警告を発行することが適切です。警告に注意してコードを修正するのはあなた次第です。
編集:言語の構文は誤解を招くコードを書くことを許可していることですが、コンパイラとその警告も言語の一部であることを覚えています。この言語を使用すると、コンパイラが疑わしいと見なすことができますが、問題が発生する可能性があることを確認するための警告が表示されます。
すでに公開されているすべてのコードのために、これをエラーにすることはできません。
エラーになるはずです。 いくつかの警告をエラーにアップグレードするためのコンパイラのオプション/プロファイルがあるはずです。
更新: 1.4で assert キーワードを導入すると、古いコードとの互換性の問題が同様に発生する可能性があるため、ソースモードを明示的に「1.4」に設定した場合にのみ使用可能。新しいソースモード「java 7」でエラーになる可能性があると思います。しかし、私は彼らがそれをすることを疑います、それが引き起こすすべての面倒を考慮して。他の人が指摘したように、混乱を招くコードを書くことを防ぐことは厳密には必要ありません。また、Javaの言語変更は、この時点で厳密に必要なものに限定する必要があります。
簡単な回答-言語で許可されているため、エラーではありません。
これがエラーではない同じ論理の場合:
public class X
{
public static void foo()
{
}
public void bar()
{
foo(); // no need to do X.foo();
}
}
コンパイラの観点から見ると、本当に重要なことは、シンボルを解決できることです。静的メソッドの場合、特定のオブジェクトに関連付けられていないため、どのクラスを検索するかを知る必要があります。 Javaの設計者は、オブジェクトのクラスを決定できるため、オブジェクトのインスタンスからそのオブジェクトの静的メソッドのクラスも解決できることを明らかに決定しました。彼らはこれを許可することを選択します-おそらく@TofuBeerの観察によって揺らいでいます-プログラマーにいくらかの便利さを与えるために。他の言語設計者はさまざまな選択を行っています。私はおそらく後者のキャンプに落ちたでしょうが、それは私にとってそれほど大したことではありません。おそらく@TofuBeerが言及している使用法を許可しますが、インスタンス変数からのアクセスを許可しないという私の立場を許可したことは、あまり受け入れられません。
これは仕様の一部であるためエラーではありませんが、当然のことながら、理論的根拠について尋ねているのです。
私の推測では、この原因は実際にはクラスのメソッドが同じクラスの静的メソッドを手間をかけずに呼び出すことを許可していることです。 x()の呼び出しは(自己クラス名がなくても)合法であるため、this.x()の呼び出しも合法である必要があり、したがって、任意のオブジェクトを介した呼び出しも合法になりました。
これにより、ユーザーが状態を変更しない場合にプライベート関数を静的に変換することもできます。
さらに、コンパイラは一般に、エラーが直接的なエラーにつながる可能性がない場合、エラーの宣言を回避しようとします。静的メソッドは状態を変更したり、呼び出しオブジェクトを気にしたりしないため、実際のエラー(混乱のみ)を引き起こしてこれを許可することはありません。警告で十分です。
インスタンス変数参照の目的は、静的を囲む型のみを提供することです。 instance.staticMethodまたはEnclosingClass.staticMethodを介してstaticを呼び出すバイトコードを見ると、同じinvoke staticメソッドバイトコードが生成されます。インスタンスへの参照は表示されません。
答えは、なぜそこにあるのかということです。クラスを使用する限り。インスタンスを介してではなく、将来の混乱を避けるのに役立ちます。
おそらく、IDEで変更できます(Eclipseの設定-> Java->コンパイラ->エラー/警告)
オプションはありません。 Javaでは(他の多くの言語と同様に)、クラス名またはそのクラスのインスタンスオブジェクトを介して、クラスのすべての静的メンバーにアクセスできます。それはあなたとあなたのケースとソフトウェアソリューションに依存し、どちらを使用すればより読みやすくなります。
これを検討するだけです:
instanceVar.staticMethod();
これを簡略化する:
instanceVar.getClass().staticMethod();
常にこれを行う必要がある場合:
SomeClass.staticMethod();
その場合、静的メソッドの継承を活用することはできません。
つまり、インスタンスを介して静的メソッドを呼び出すことにより、コンパイル時にインスタンスがどの具象クラスであるかを知る必要はなく、継承チェーンのどこかにstaticMethod()を実装するだけです。
編集:この答えは間違っています。詳細についてはコメントをご覧ください。