2

Javaでは、コンストラクターを再帰的にすることはできません。コンパイル時エラー:「再帰的なコンストラクターの呼び出し」。この制限がなかったとしましょう。

覚えておくべきこと:

  • コンストラクターの戻り型はvoidです。これは無効な方法であるため、再帰の完全な力を利用することはできません。
  • コンストラクターは、this()を使用して自分自身(または他のコンストラクター)を呼び出すことができます。しかし、「これへの呼び出しはコンストラクターの最初のステートメントでなければなりません」
  • 連続する呼び出しの間に非ローカルデータを使用して、再帰的なコンストラクターから利益を得ることができます。

再帰的なコンストラクターを許可することで何かメリットはありますか?

4

6 に答える 6

9

コンストラクター (互いに呼び出している場合) は、 を返すメソッドのようなものvoidです。したがって、それらが結果を生み出すことができる唯一の方法は、副作用によるものです。これは、構築しているオブジェクトを変更するか、パラメーターとして渡された値を変更することによって制限されます。後者は、コンストラクターではかなり厄介なアイデアです。コンストラクターは通常、パラメーターを変更せずにパラメーターから情報を取得します。

そのため、構築中のオブジェクトを変更することが、再帰の進行状況を追跡して最終的に終了させるための唯一のオプションです。そして、通常のコンストラクター内の単純なループよりも、それがどのように書きやすく、読みやすくなるのかを理解するのは非常に困難です。

コンストラクター内から( を使用して) 別のコンストラクターを呼び出すことは、もちろん、コンストラクター内で式thisを使用することとはまったく異なります。new

class Node
{
    Node _left, _right;

    public Node(Node left, Node right)
    {
        _left = left != null ? new Node(left._left, left._right) : null;
        _right = right != null ? new Node(right._left, right._right) : null;
    }
}

ここで、Nodeコンストラクターは自分自身を呼び出しますが、新しい式を介して. これが決定的な違いです。式は値を生成するnewため、これは純粋に「機能的」で変更のないものであり、ノードのツリーのディープ コピーを作成する便利な方法を提供します。

于 2010-04-07T18:39:00.497 に答える
1

この問題を見てみましょう。まず、 を呼び出すとどうなりますnew MyClass("foo");か? さて、2つのことが起こっています。まず、仮想マシンは type のオブジェクトを格納するために必要なメモリを割り当てますMyClass。次に、コンストラクターが呼び出されます。コンストラクターの仕事は、この割り当てられたばかりのメモリを初期化することです。したがって、コンストラクターには戻り値の型がまったくありません (void でさえありません)。new 演算子によって返される値は、割り当てられたメモリへの参照であるため、コンストラクターも同様に返すことはできません。

次に、再帰的なコンストラクター呼び出しの利点は何でしょうか。このような呼び出しの唯一の利点は、特定のコンストラクター パラメーターを他のパラメーターと同様に処理し、コンストラクターを再呼び出しすることです。これは可能ですが、コンストラクター自体の値を (非最終パラメーターを使用して) 調整し、その後でオブジェクト属性を初期化するだけで一般的に簡単です (要するに、これには再帰は必要ありません)。

第 2 に、必要なだけ再帰できるワーカー メソッドにすべての作業をオフロードすることで、かなり簡単に再帰を行うことができます。

より興味深い質問は、コンストラクターの最初のステートメントである super または this 呼び出しの制限です。この制限は、ずさんな、または安全でないプログラミング手法を思いとどまらせるために導入されたものと思われます。ただし、この制限を回避することは (美しくはありませんが) 可能であるため、ここではステートメントを太字で示しています。式には副作用 (変数の代入など) があり、呼び出し自体の前にパラメーターに使用される式が呼び出されることを覚えている場合は、デリゲート コンストラクターを呼び出す前にすべての計算を行う複雑な式を作成することができます。

コンストラクター本体の後半でデリゲート/スーパー コンストラクターの呼び出しが必要になる一般的な理由は、パラメーター操作です。これらの計算を行い、正しい値を提供する (静的) ヘルパー関数を使用して、これを行うことができます。これは一般的にきれいですが、すべての場合ではありません。ホットスポットはこれらを非常にうまくインライン化できるため、実際の実行速度は影響を受けません。

つまり、最終的には、デリゲート/スーパー コールを自由に配置する柔軟性を提供することと、誤ったプラクティスを非常に困難にすることによって提供される追加の安全性を提供することを検討することになります。Java の設計者 (および一般的な Java の哲学) が下した選択は、専門家の手による生の言語能力 (複雑さの増加) を犠牲にして、間違ったことを実行しにくくすることです。個人的にはパワーが欲しいのですが、私にとっては有効な選択です(これらの制限のないJava++言語をJVMにいつでも実装できます)。

于 2010-04-08T09:26:21.037 に答える
1

Constructors can be recursive. (That's in C#, but you can do the same thing in Java)

于 2010-04-07T18:47:46.893 に答える
0

再帰コンストラクターを作成できない場合がありますが、コンストラクターから再帰関数を呼び出すことはできます。これまでにこれを行う必要があったことはありません。また、あなたが必要とする状況は考えられませんが、必要に応じて行うことができます。

于 2010-04-07T18:39:07.200 に答える
0

コンストラクターの戻り値の型は void です。

いいえ、そうではありません。

コンストラクターは、 this() を使用して自分自身 (または他のコンストラクター) を呼び出すことができます。

いいえ。他のコンストラクターを呼び出すことができるのは、それが現在のコンストラクターの再帰呼び出しにつながらない場合のみです。そのため、参照したエラー メッセージが表示されます。

連続する呼び出しの間に非ローカル データを使用して、再帰コンストラクターから得られる可能性のある利益を得ることができます。

どのように?オブジェクトを初期化する必要があるのはなぜですか? 1回のパスで順番にできないのはいつですか?この問題は、コンピューター プログラミングの 39 年間、オブジェクト指向の 20 年間で一度もありませんでした。

再帰コンストラクターを許可するメリットはありますか?

あなたは何も思いつきませんでした...

于 2010-04-08T08:49:32.173 に答える
0

許すってどういうこと?Java では再帰コンストラクターを使用できます。コードを再利用して、より階層的な方法でコンストラクターを設計できます。

次の再帰コンストラクターの例では、new User()ornew User("Marcus")を呼び出すことができ、使用するいずれかのコンストラクターで を にnewUser設定しtrueます。

public class User() {
  public String userName;
  public boolean newUser;
  User() {
    newUser = true;
  }
  User(String userName) {
    // Recursively call no-argument constructor
    this();
    this.userName = userName;
  }
}

これは、再帰コンストラクターを使用しない場合と同じです。コードの重複行に注意してください。

public class User() {
  public String userName;
  public boolean newUser;
  User() {
    newUser = true;
  }
  User(String userName) {
    newUser = true;
    this.userName = userName;
  }
}

次の再帰的でないコンストラクターの例では、コンストラクターに名前を渡さない場合、名前は "New User" に設定されます。名前を設定していない場合にのみ、引数なしのコンストラクターを呼び出したいと思います。ここで再帰的なコンストラクター呼び出しを行うと、userName を 2 回設定することになります。

public class User() {
  public String userName;
  User() {
    this.userName = "New User";
  }
  User(String userName) {
    this.userName = userName;
  }
}

次の場合にのみ、再帰コンストラクターを使用します。

  1. 複数のコンストラクターを持つ
  2. コンストラクターにコードを含める
  3. 別のコンストラクターにあるコードを再帰的に使用したい
于 2010-04-07T19:14:38.793 に答える