44361


Javaの総称型について教えてください。

以下のようなコードを書きましたが、コンパイルエラーとなりました。

[code]

    class Hoge {}
    
    class Test<T> {
        Test() {
            // error: incompatible types: Hoge cannot be converted to T
            T hoge = new Hoge();
        }
    }
    
[/code]

そこで、総称型TをHogeという具体的なクラスに限定すればよいのでは、と考えて、以下のように修正しました。

[code]

    class Test<T extends Hoge> {
    
[/code]

しかし、同じコンパイルエラーが出てしまいます。

どのようにすれば実現できますでしょうか。（そもそも総称型にしている意味が無い、というご指摘はあるかと思いますが）

* * *

質問者追記：質問の発端となった具体的なコード

文章でうまく表現できている自信がありませんが、やりたいことは以下になります。

  * 複数のアプリケーションに共通する処理は親クラスで共通部品として用意する
  * それを継承する各アプリケーションは、ユーザAまたはユーザBというクラスを使う
  * ユーザAとユーザBのどちらを使うのかは、アプリケーションによって事前に決まっている
  * ユーザAとユーザBが持つメソッドは異なる
  * ユーザのインスタンスは親クラスのメンバで持ちたい
  * そのインスタンスを生成するのも、親クラスでやりたい（ここで躓いた）

具体的なコードは以下になります。

**共通部品としてあらかじめ用意しておくクラス**

[code]

    // アプリケーションの流れを Template Method パターンで用意
    abstract class AppBase<T extends UserBase> {
        // ユーザの種類はサブクラスが決める
        T user;
    }
    
    // ユーザの基底クラス
    class UserBase {}
    
    // ユーザA
    class UserA extends UserBase {
        // ユーザAは独自のメソッドmethodAを持っている
        void methodA() {}
    }
    
    // ユーザB
    class UserB extends UserBase {
        // ユーザBは独自のメソッドmethodBを持っている
        void methodB() {}
    }
    
[/code]

**上記部品を使って実装するイメージ**

[code]

    // ユーザA向けのアプリケーションA
    class AppForUserA extends AppBase<UserA> {
        public AppForUserA() {
            // ユーザAだけが持っている、user.methodA() を使った処理
        }
    }
    
    // ユーザA向けのアプリケーションB
    class AppForUserB extends AppBase<UserB> {
        public AppForUserB() {
            // ユーザBだけが持っている、user.methodB() を使った処理
        }
    }
    
[/code]

ここで、`AppBase` の `user` メンバにユーザクラスのインスタンスを代入したいと思い、

[code]

    abstract class AppBase<T extends UserBase> {
        T user;
        public AppBase() {
            T user = new T();
            // -> error: unexpected type
        }
    }
    
[/code]

としようとしたところ無理であることを知り、そこで試しに、それ自体に意味はありませんが

[code]

    abstract class AppBase<T extends UserBase> {
        protected T user;
            public AppBase() {
            T user = new UserA();
            // -> error: incompatible types: UserA cannot be converted to T
        }
    }
    
[/code]

としてみてもエラーとなったため、質問した次第です。


CC BY-SA 4.0
2018-05-29T08:42:44.340
44360
2018-05-30T04:07:37.650
2018-05-30T02:54:38.793
19759
19759
question
java
総称型の変数に対して、extendsで限定してもインスタンスを代入できない


代入の場合は、 extends ではなく super を用いる必要があるはずです。

* * *

そして、もう少し調べました。

例えば、今回のケースで言えば、以下のようなことが、ひとまずやれたらいいな、と思うことかもしれません:

[code]

    class Hoge {}
    
    class Test<T super Hoge> {
        Test() {
            T hoge = new Hoge();
        }
    }
    
[/code]

しかし、その実、これはコンパイルが通りません。参考： <https://stackoverflow.com/questions/37411256/why-
super-keyword-in-generics-is-not-allowed-at-class-level>

これは、 java の generics は、コンパイルした直後に generics
情報が消えることと関係していると思います。このコードをコンパイルしようとしたとき、その型的な制約は、「T は任意の Hoge
の上位クラスたちすべてに対して成立するようなコードのみを許可する。つまり、メソッドコールであれば Hoge
の上位クラスがすべて持っているようなメソッドに限るし、代入であれば Hoge の上位クラスすべてが代入可能な型に対してのみ代入されなければならない」です。

これは、結果として何がおこるかというと、それって T はひとまず Object として取り扱ってこの file
をコンパイルしてしまうのと、できることはまったくかわらなくなります。なので、 class に対するジェネリクスを定義する際に、 super
制約は付ける意味がない、だからいらない。禁止。という論理だと思っています。

* * *

追記された条件でしたら、私だったら、以下のようにすると思います。

  * AppBase に abstract な T を返す createUser メソッドを追加
  * 各 個別 App で createUser 実装


CC BY-SA 4.0
2018-05-29T08:53:41.997
44361
2018-05-30T04:07:37.650
2018-05-30T04:07:37.650
754
754
44360
answer