web-dev-qa-db-ja.com

なぜJavaのコンストラクタに戻り値の型がないのですか?

可能性のある複製:
コンストラクターが値を返さない理由

コンストラクターに戻り型があり、無効でもないのはなぜですか?その理由は何ですか?

43
user845932

コンストラクタは、内部的には_<init>_およびvoid戻り値の名前を持つ非静的メソッドです。何も返しません。内部的に最初のオブジェクトが割り当てられ、そのコンストラクタが呼び出されます。オブジェクトは、コンストラクタ自体では割り当てられません。
つまり、構文new Object()はコンストラクターを呼び出すだけでなく、新しいオブジェクトを作成し、コンストラクターを呼び出した後にそれを返します。 Suns 'Java tutorial は、「new演算子の後に、新しいオブジェクトを初期化するコンストラクターの呼び出しが続く」ことを意味します。初期化は作成を意味しません。

質問に答えます。戻り値の型宣言がないと、コンストラクターをメソッドと区別する方法になります。ただし、コンストラクターからはvoidメソッドから返すことができます。たとえば、次のコードは正しくコンパイルおよび実行されます。

_public class TheClass {
    public TheClass(){
        return;
    }
    public void TheClass(){ //confusing, but this is void method not constructor
        return;
    }

    public static void main(String[]a){
        TheClass n = new TheClass();
        n.TheClass();//void method invocation
    }
}
_

このクラスには、1つのvoidメソッド(自宅で試さないでください-大文字のメソッドは悪いスタイルです)と1つのコンストラクターがあります。違いは、宣言された戻り値の型にあります。

コンストラクターが非静的voidメソッドであることを示すこのJNIコードスニペットを見てください。

_ jstring
 MyNewString(JNIEnv *env, jchar *chars, jint len)
 {
     jclass stringClass;
     jmethodID cid;
     jcharArray elemArr;
     jstring result;

     stringClass = (*env)->FindClass(env, "Java/lang/String");
     if (stringClass == NULL) {
         return NULL; /* exception thrown */
     }
 /* Get the method ID for the String(char[]) constructor */
     cid = (*env)->GetMethodID(env, stringClass,
                               "<init>", "([C)V");
     if (cid == NULL) {
         return NULL; /* exception thrown */
     }

     /* Create a char[] that holds the string characters */
     elemArr = (*env)->NewCharArray(env, len);
     if (elemArr == NULL) {
         return NULL; /* exception thrown */
     }
     (*env)->SetCharArrayRegion(env, elemArr, 0, len, chars);

     result = (*env)->AllocObject(env, stringClass);
     if (result) {
         (*env)->CallNonvirtualVoidMethod(env, result, stringClass,
                                          cid, elemArr);
         /* we need to check for possible exceptions */
         if ((*env)->ExceptionCheck(env)) {
             (*env)->DeleteLocalRef(env, result);
             result = NULL;
         }
     }
     /* Free local references */
     (*env)->DeleteLocalRef(env, elemArr);
     (*env)->DeleteLocalRef(env, stringClass);
     return result;
 }
_

特にこれらのフラグメント:

_ /* Get the method ID for the String(char[]) constructor */
 cid = (*env)->GetMethodID(env, stringClass, "<init>", "([C)V");
_

その後

_ /* Allocate new object. */
 result = (*env)->AllocObject(env, stringClass);
 if (result) {
      /* Call uninitialized objects' constuctor. */
      (*env)->CallNonvirtualVoidMethod(env, result, stringClass, cid, elemArr);
_

最初のオブジェクトが割り当てられ、次に非静的_<init>_メソッドが呼び出されます。詳細については、 こちら をご覧ください。 AllocObject function documentation は、「オブジェクトのコンストラクターを呼び出さずに新しいJavaオブジェクトを割り当てます。オブジェクトへの参照を返します」という意味です。したがって、JVMでは、オブジェクトはコンストラクタによって割り当てられず、初期化されるだけです。コンストラクタのバイトコードを見ると、オブジェクトが返されていないことがわかります(voidメソッドとまったく同じです)。

別の方法として、サンプルクラスを分解すると、そのコンストラクターから親(Object)コンストラクターの呼び出しが表示されます。

_#javap -c NewClass
Compiled from "NewClass.Java"
public class NewClass extends Java.lang.Object{
public NewClass();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method Java/lang/Object."<init>":()V
   4:   return

}
_

_<init>_メソッドは、実際にはJava言語の一部ではないことに注意してください。むしろ、それはJava仮想マシンがJavaクラスファイルで見ることを期待しているものです。 Java言語はクラスファイルに依存しないため、この区別は重要です。 Javaソースは、ネイティブ実行可能ファイルを含む他のバイナリ形式にコンパイルできます。 Java言語ソースを他のバイナリ形式に変換するJavaコンパイラは、オブジェクトが適切な方法で適切に初期化される限り、_<init>_という名前のメソッドを生成する必要はありません。時間。 Java Language Specification(JLS)は、初期化の順序とそれがいつ発生するかを詳述していますが、実際にどのように達成されるかは述べていません。

しかし、ここでJVMについて話していることがわかります。

一部の非信者にとって、これはオブジェクトが存在し、コンストラクターから戻る前に割り当てられることを示す例(thx biziclop)です。

_   class AnotherClass {

        private String field;
        public static AnotherClass ref;

        public AnotherClass() {
            this.field = "value";
            AnotherClass.ref = this;
            throw new RuntimeException();
        }

        @Override
        public String toString() {
            return field;
        }
    }

    public class MainClass {
        public static void main(String[] a) {
            try {
                new AnotherClass();
                return;
            } catch (RuntimeException ex) {
                System.out.println("exception");
            }
            System.out.println("instance: " + AnotherClass.ref);
        }
    }
_
49
zacheusz

どのようにして戻り値を取得しますか?どのような価値に興味があり、返されますか?戻り値の型をどのように宣言しますか?

 X x = new X ();

xにX参照を割り当てます。今、new Xは何かを返しますが、どのように取得する必要がありますか?

 class X { 
     public int X () { 
          return 42;
     }
 }

俳優から何かを返すロジックは何ですか?エラーメッセージ?いくつかのloginfo?後でポーリングするファイルまたは属性に書き込みます。

アクターはオブジェクトごとに1回しかアクセスされないため、別の戻り値を使用するために考えられる唯一の理由は、作成プロセス自体について通知することです。

 class X { 
     private Y y;
     public int X () { 
          y = new Y ();
     }
     public Y getY () { return y; }
 }
3
user unknown

VMコンストラクターの実装は値を返しませんが、実際には、新しいオブジェクトの参照のようなものを返します。新しいオブジェクトの参照と追加の戻り値の一方または両方を1つのステートメントに格納します。

1
Ben