9

これはJavaで可能です。

package x;

public class X {

    // How can this method be public??
    public Y getY() {
        return new Y();
    }
}

class Y {}

getY()では、Javaコンパイラがメソッドを次のように宣言できる理由は何publicですか?私を悩ませているのは、クラスYはパッケージプライベートですが、アクセサーgetY()はメソッドシグネチャでそれを宣言しているということです。しかし、パッケージの外では、メソッドの結果を以下xに割り当てることしかできません。Object

// OK
Object o = new X().getY();

// Not OK:
Y y = new X().getY();

わかった。これで、どういうわけか、これがメソッド結果の共分散で説明できる例を作成することができます。しかし、事態をさらに悪化させるために、私はこれを行うこともできます:

package x;

public class X {
    public Y getY(Y result) {
        return result;
    }
}

class Y {}

今ではパッケージgetY(Y result)の外から電話をかけることはできませんでした。xなぜ私はそれをすることができますか?コンパイラがメソッドを呼び出せない方法で宣言できるのはなぜですか?

4

5 に答える 5

6

Java の設計には多くの考慮が払われていますが、最適ではない設計が見過ごされることがあります。有名な Java Puzzlers がそれを明確に示しています。

別のパッケージは、引き続き package-private パラメーターを使用してメソッドを呼び出すことができます。最も簡単な方法は、それを渡すことnullです。しかし、そのような構造が本当に理にかなっているのは、まだそれを呼び出すことができるからではありません。これは package-private の背後にある基本的な考え方を壊します: パッケージ自体だけがそれを見るべきです。ほとんどの人は、この構造を利用するコードは少なくとも紛らわしく、単に悪臭を放っているだけだということに同意するでしょう。許さないほうがよかった。

補足として、それが許可されているという事実は、いくつかのコーナーケースを開きます. たとえば、Arrays.asList(new X().getY())コンパイルを実行する別のパッケージから、アクセスできない Y クラスの配列を作成しようとするため、実行時に IllegalAccessError がスローされます。これは、このアクセスできない型のリークが、言語設計の残りの部分が行う仮定に適合しないことを示しています。

しかし、Java の他の珍しい規則と同様に、Java の最初のバージョンでは許可されていました。それは大したことではなく、下位互換性は Java にとってより重要であるため、この状況を改善する (許可しない) ことはもはや価値がありません。

于 2011-03-10T22:16:57.123 に答える
2

まず、メソッドを呼び出すことができます。簡単な例は、同じパッケージ内でそれを呼び出すことです。

自明ではない例:

package x;
public class Z extends Y {}

package x2;
    x.getY( new Z() ); // compiles

しかし、それは重要ではありません。

要点は、Java は明らかに無意味な設計の一部を禁止しようとしますが、すべてを禁止することはできないということです。

  1. 無意味なことは何ですか?それは非常に主観的です。

  2. 厳しすぎると、物事がまだプラスチックであるときに開発に悪影響を及ぼします。

  3. 言語仕様はすでに複雑すぎます。ルールをさらに追加することは、人間の能力を超えています。[1]

[1] http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.6

于 2011-03-10T21:32:56.890 に答える
2

ではない型のインスタンスを返す public メソッドがあると便利な場合がありpublicます。たとえば、この型がインターフェイスを実装している場合などです。多くの場合、ファクトリは次のように機能します。

public interface MyInterface { }

class HiddenImpl implements MyInterface { }

public class Factory {
    public HiddenImpl createInstance() {
        return new HiddenImpl();
     }
}

もちろん、この場合、コンパイラーが の戻り値を強制できると主張することもできcreateInstance()ますMyInterface。ただし、それを許可することには、少なくとも 2 つの利点がありますHiddenImpl。1 つは、HiddenImpl複数の個別のインターフェイスを実装でき、呼び出し元は戻り値を使用するタイプを自由に選択できることです。もう 1 つは、パッケージ内からの呼び出し元が同じメソッドを使用して HiddenImpl のインスタンスを取得し、それをキャストしたり、ファクトリに 2 つのメソッド (1 つの publicMyInterface createInstance()と 1 つの package-protected HiddenImpl createPrivateInstance()) を持たせたりする必要なく、それをそのまま使用できることです。同じこと。

のようなものを許可する理由public void setY(Y param)は似ています。の public サブタイプが存在する場合がありY、パッケージの外部からの呼び出し元がこれらのタイプのインスタンスを渡す場合があります。ここでも、上記と同じ 2 つの利点が適用されます (このようなサブタイプがいくつか存在する場合があり、同じパッケージからの呼び出し元がYインスタンスを直接渡すことを選択する場合があります)。

于 2011-03-10T21:37:32.197 に答える
1

次のようなことはできませんでした:

Object answer = foo1.getY();  
foo2.setY(  foo1.getY().getClass().cast(answer)  );

はい、それは醜くて愚かで無意味ですが、それでもできます。

そうは言っても、元のコードはコンパイラの警告を生成すると思います。

于 2011-03-10T21:03:54.763 に答える
1

許可する大きな理由は、不透明な型を許可することです。次のシナリオを想像してください。

package x;

public interface Foo;

class X
{
    public Foo getFoo ( )
    {
        return new Y( );
    }

    class Y implements Foo { }
}

ここにあなたの状況 (パブリック API を介してエクスポートされたパッケージ保護された内部クラス) がありますが、呼び出し元に関する限り、返される Y は不透明な型であるため、これは理にかなっています。とはいえ、IIRC NetBeans はこの種の動作に対して警告を発します。

于 2011-03-10T19:59:41.487 に答える