[ TIL ] Generics

29240 ワード


ギネス世界記録とは?


様々なタイプのオブジェクトを処理する方法や集合クラスでコンパイルするときのタイプチェックの機能.
タイプパラメータとして理解するのは簡単です.
私たちがよく知っているArrayListもギネス世界記録を使っています
List<String> strList = new ArrayList<String>();
strList.add("string");
strList.add(1); // 컴파일러가 에러를 띄운다. <> 에 선언된 타입과 다르기 때문이다.

ギネス類の宣言


ギネス世界記録クラスを簡単に作成しましょう.
class MyClass<T> {
	T element;
    
    	void setElement(T element){
        	this.element = element;
        }
        T getElement(){
        	return element;
        }
}
Tはタイプパラメータと呼ばれ、Tではなく他のパラメータを使用することができる.
例えば、ArrayListはEを使用する.
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ただ文字が違うだけで、意味は同じです.
MyClassのオブジェクトを作成する場合は、次のタイプを指定する必要があります.
MyClass<String> cls = new MyClass<String>();
cls.setElement("element");
cls.setElement(123); // 에러. String 이외의 타입은 지정 불가.

String element = cls.getElement();
MyClassのTをタイプ変数,MyClassを元のタイプと呼ぶ.

ギネス世界記録の制限


静的メンバーでは使用できません
タイプ変数Tは静的メンバーには使用できません.
Tはインスタンス変数と見なされるからである.
class MyClass<T> {
	static T element; // 에러
}
考えてみれば当然だ.
オブジェクトを作成する必要がなく、静的メンバーが使用可能である必要があります.
しかし,Genericクラスのオブジェクトを生成する前に,Tのタイプが何であるかは知らなかった.
ムカデアレイを作成できません
ムカデ配列タイプの参照変数を宣言できます.
newt[10]のように配列を生成することはできない.
class Limit<T> {
    T[] elements;
    
    T[] toArray(){
        T[] tmpArr = new T[elements.length]; // 에러
        ...
        return tmpArr;
    }
    
    ...
}
これはnew演算子のためであり,new演算子はコンパイル時にタイプTが何であるかを正確に知らなければならない.
同じ理由でinstanceof演算子も被演算子としてtを使用できません.理由はnew演算子と同じです.

ムカデ類を使用して作成


ギネスクラスの作成


Genericクラスでオブジェクトを作成する場合、
変数とオブジェクトのタイプ変数は完全に一致する必要があります.
ArrayList<Parent> list = new ArrayList<Child>(); // 에러
ArrayList<Parent> list = new ArrayList<Child>();
2つの地理クラスのタイプは継承関係に属し、代入のタイプは同じでよい.
Parent<String> a = new Child<String>();

ギネス類の使用


代替タイプや他のタイプのオブジェクトは追加できません.
List<Car> list = new ArrayList<Car>();
list.add(new Car());
list.add(new Bike()); // 에러
代入されたタイプと追加されたタイプに継承関係がある場合は、どうでもいいです.
List<Vehicle> list = new ArrayList<Vehicle>();
list.add(new Car()); // 가능. Car는 Vehicle 의 자손
list.add(new Bike()); // 가능. Bike도 Vehicle의 자손

ギネスを制限する


特定のタイプの子孫のみをタイプ変数として受け入れる


extendsを使用すると、特定のタイプの子孫のみが代入できるように制限できます.
class MyClass<T extends Vehicle> {
	T vehicle;
    	void setVehicle(T vehicle){
        	this.vehicle = vehicle;
        }
}
MyClassのタイプ変数を使用してVerhicleのサブクラスではなくクラスを指定すると、エラーが発生します.
MyClass<Car> cls1 = new MyClass<Car>(); // 가능. Car는 Vehicle의 자손
MyClass<String> cls2 = new MyClass<String>(); 
// 에러. String 은 Vehicle의 자손이 아니다.

特定のクラスであるサブクラスと、特定のインタフェースを実装するクラスのみを受信します。


Vehicleの子とプロダクトインタフェースをタイプ変数として実装するクラスのみを使用する場合は、次のように「和」を使用します.
class MyClass<T extends Vehicle & Product> {
	//...
}

ワイルドカード


次のコードを見てください.
class Rider {

    String name;
	
    public Rider(String name){ this.name = name; }
    
    void run(Action<Vehicle> action){
        System.out.println(name + " is ready to run");
        action.run();
    }
}
runメソッドでは,動作対象を受け入れ,対応する騎乗を運転する.
しかしrunのパラメータから,Actionのタイプ変数はVehicleに固定されている.
前述したように、タイプ変数は継承関係に関係なく一致する必要があります.
次のコードが間違っています
Rider rider = new Rider("Mike");
Action<Car> carAction = new Action<Car>(new Car("Lamborghini"));
rider.run(carAction);
タイプ変数としては,無条件にVerhicleのみを受け入れ,CarやBikeなどの子孫は受け入れられない.
CarとBikeを手に入れて、騎士を自由に乗り換えて走らせたいなら、どうすればいいですか?
ワイルドカード(?)使って解決した.
class Rider {

    String name;
	
    public Rider(String name){ this.name = name; }
    
    //와일드 카드 사용
    void run(Action<? extends Vehicle> action){
        System.out.println(name + " is ready to run");
        action.run();
    }
}
ワイルドカードでは、次の上限と下限を設定できます.
<? extends T> T와 그 자손들만 가능
<? super T>   T와 그 조상들만 가능
<?> 	      모든 타입이 가능. 
たとえば、Riderクラスのrunメソッドのパラメータを変更すると、次のようになります.
タイプ変数として指定できるのは、Carとその祖先(Verhicle,Object)のみです.
void run(Action<? extends Vehicle> action){
	System.out.println(name + " is ready to run");
        action.run();
}
また、ギネスクラスとは異なり、ワイルドカードは使用できません&extendsの後に複数のクラスを指定します.

ギネス世界記録


ギネス世界記録


方法の宣言部はギネスタイプの方法を宣言した.
たとえば、Collections.sort()があります.
// 지네릭 메서드의 지네릭 타입의 선언 위치는 반환타입 바로 앞이다.
static <T> void sort(List<T> list, Comparator<? super T> c)

ムカデの使い方


上のRiderクラスのrunメソッドを静的ムカデメソッドに変更します.
class Rider {
    static <T extends Vehicle>void run(Action<T> product){
        System.out.println("Rider is ready to run");
        product.run();
    }
}
ギネス世界記録の方法は体力でも大丈夫です.方法内でしか使わないので.
上記のrunメソッドを使用するには、メソッドを呼び出すときにタイプを指定する必要があります.
ただし、ほとんどの場合、導入のタイプを推定できるため、省略できます.
Action<Car> carAction = new Action<>(new Car("Lamborghini"));
Rider.<Car>run(carAction);

ムカデタイプの除去


コンパイラは、バイナリタイプを使用してソースファイルをチェックします.
そして必要な場所に変形を加えます.
そしてムカデタイプを取り除きます.
つまり、コンパイルされたファイルにはギネスタイプに関する情報はありません.
これは,Generic導入以前のソースコードとの互換性を保つためである.

基本削除プロセス


  • ムカデ型の境界を取り除く.
    T extends Verhicleの場合、RはVerhicleに変換されます.
    TであればObjectに変換されます.

  • ギネスタイプを削除すると、タイプが一致しない場合は変形が追加されます.