ホームページ  >  記事  >  Java  >  Javaのジェネリックスの詳細な説明

Javaのジェネリックスの詳細な説明

高洛峰
高洛峰オリジナル
2017-01-18 10:54:111383ブラウズ

いわゆるジェネリック: クラスとインターフェイスを定義するときに型パラメーターを指定できます。この型パラメーターは、変数を宣言し、オブジェクトを作成するときに決定されます (つまり、型引数とも呼ばれる実際の型パラメーターを渡します)。ジェネリック クラスまたはインターフェイス

"ダイヤモンド" 構文

//定义
 
public interface List<E> extends Collection<E>  
 
public class HashMap<K,V> extends AbstractMap<K,V>  implements Map<K,V>, Cloneable, Serializable 
//使用
 
List<String> list = new ArrayList();
 
//Java7以后可以省略后面尖括号的类型参数
 
List<String> list = new ArrayList<>();

ジェネリック クラスからサブクラスを派生する

//方式1
 
public class App extends GenericType<String>
 
//方式2
 
public class App<T> extends GenericType<T>
 
//方式3
 
public class App extends GenericType

疑似ジェネリック

真のジェネリック クラスは存在せず、ジェネリック クラスは Java 仮想マシンに対して透過的です。JVM は認識しません。ジェネリック クラスの存在。つまり、JVM はジェネリック クラスを通常のクラスと何ら区別せずに扱います。そのため、静的メソッド、静的初期化ブロック、および静的変数では型パラメータを使用できません。

- 以下のメソッドはすべて間違っています

private static T data;
 
static{
 
    T f;
 
}
 
public static void func(){
 
    T name = 1;
 
}

次の例は、ジェネリック クラスが存在しないことを側から検証できます

public static void main(String[] args){
 
        List<String> a1 = new ArrayList<>();
        List<Integer> a2 = new ArrayList<>();  
    System.out.println(a1.getClass() == a2.getClass());
 
    System.out.println(a1.getClass());
 
    System.out.println(a2.getClass());
 
}

Output

true
 
class java.util.ArrayList
 
class java.util.ArrayList

Type wildcard

まず第一に、Foo が親であるかどうかを明確にする必要がありますBar のクラスですが、List4ee996100bf04ab273e687539c860625 は Listad40e550a33cb99ea30eede96e03e60e の親クラスではありません。Java では、汎用のワイルドカードを表すために「?」を使用します。この種のワイルドカードを使用した .List ジェネリックは、要素を設定 (set) することはできませんが、要素を取得 (get) することしかできません。プログラムはリスト内のタイプを判断できないため、オブジェクトを追加できません。ただし、取得されるオブジェクトは Object 型である必要があります。

次のメソッドはコンパイル エラーになります:

List<?> list = new ArrayList<>();
 
list.add(new Object());

いくつかのアイデア:

1. Listf7e83be87db5cd2d9a8a0b8117b38cd4 オブジェクトは Listf7e83be87db5cd2d9a8a0b8117b38cd4 オブジェクトとして使用できません。 ;オブジェクト>クラスのサブカテゴリ。

2. 配列とジェネリックは異なります。Foo が Bar のサブタイプ (サブクラスまたはサブインターフェイス) であると仮定すると、Foo[] は依然として Bar[] のサブタイプですが、 G4ee996100bf04ab273e687539c860625 は Gad40e550a33cb99ea30eede96e03e60e ではありません。

3. さまざまなジェネリック リストの親クラスを表すには、型ワイルドカードを使用する必要があります。List コレクションに型引数として疑問符を渡します。 ;?>( 不明なタイプの要素のリストを意味します)。この疑問符 (?) はワイルドカード文字と呼ばれ、その要素の型は任意の型に一致します。

ワイルドカードの上限

List41e72a075beaa92298ffb490fa164613 は、すべての SuperType ジェネリック リストの親クラスまたはそれ自体を表します。ワイルドカードの上限を持つジェネリックスは set メソッドを持つことができず、get メソッドのみを持つことができます。

ワイルドカードの上限を設定すると、次の問題を解決できます: Dog は Animal サブクラスであり、受信リストの数を取得する getSize メソッドがあります。コードは次のとおりです

abstract class Animal {
    public abstract void run();
}
class Dog extends Animal {
    public void run() {
        System.out.println("Dog run");
    }
}
public class App {
    public static void getSize(List<Animal> list) {
        System.out.println(list.size());
    }
    public static void main(String[] args) {
        List<Dog> list = new ArrayList<>();
        getSize(list); // 这里编译报错
    }
}

ここでのプログラミング エラーの理由は次のとおりです。 List4cfe4bcd4d110814d5bcb5b26c812079 は Lista6240277e46d54dc29cc58bc214c4982 の親クラスではありません。 1つ目の解決策は、getSizeメソッドの仮パラメータListをList>に変更することですが、この場合はオブジェクトを取得するたびに強制的な型変換が必要となり面倒です。ワイルドカードの上限を使用すると、List4cfe4bcd4d110814d5bcb5b26c812079 を List2b8cf271c74fe4a8189708641290822f に変更することができ、コンパイルが間違ってしまうことはなく、型変換は必要ありません。


ワイルドカード

Listc971d711d31e0f37517255ddf72fe1f7の下限は、SubTypeジェネリックリストの下限を表します。ワイルドカードの上限を持つジェネリックには get メソッドを含めることはできず、set メソッドのみを含めます。

ジェネリックメソッド

型パラメータを使用せずにクラスまたはインターフェイスを定義するが、メソッドを定義するときに型パラメータを自分で定義したい場合、JDK1.5 ではジェネリックメソッドもサポートされています。ジェネリック メソッドのメソッド シグネチャには、通常のメソッドのメソッド シグネチャよりも多くの型パラメータ宣言が含まれます。複数の型パラメータ宣言は、すべてのメソッドの間にカンマ (,) で区切られます。修飾子とメソッドの戻り値の型の構文形式は次のとおりです:

修饰符 返回值类型 方法名(类形列表){
 
//方法体
 
}

ジェネリック メソッドでは、メソッドの 1 つ以上のパラメーター間の型の依存関係、またはメソッドの戻り値とパラメーター間の型の依存関係を表現するために型パラメーターを使用できます。このような型の依存関係がない場合は、ジェネリック メソッドを使用しないでください。 Collections の copy メソッドは、ジェネリック メソッドを使用します:

 public static <T> void copy(List<? super T> dest, List<? extends T> src){ ...}

このメソッドでは、src 型が dest 型またはそれ自体のサブクラスである必要があります。

消去と変換

厳密なジェネリック コードでは、ジェネリック宣言を持つクラスは常に型パラメーターを持つ必要があります。ただし、古い Java コードとの一貫性を保つために、型パラメータを指定せずにジェネリック宣言を持つクラスを使用することもできます。このジェネリック クラスに型パラメーターが指定されていない場合、型パラメーターは raw 型と呼ばれ、パラメーターの宣言時に指定された最初の上限型がデフォルトになります。

ジェネリック情報を持つオブジェクトをジェネリック情報のない別の変数に代入する場合、山かっこ内のすべての型情報は破棄されます。たとえば、List 型を List に変換すると、List のコレクション要素の型チェックが型変数 (つまり、Object) の上限になります。この状況を消去と呼びます。

class Apple<T extends Number>
 
{
 
 T size;
 
 public Apple()
 
 {
 
 }
 
 public Apple(T size)
 
 {
 
  this.size = size;
 
 }
 
 public void setSize(T size)
 
 {
 
  this.size = size;
 
 }
 
 public T getSize()
 
 {
 
  return this.size;
 
 }
 
}
 
public class ErasureTest
 
{
 
 public static void main(String[] args)
 
 {
 
  Apple<Integer> a = new Apple<>(6);    // ①
 
  // a的getSize方法返回Integer对象
 
  Integer as = a.getSize();
 
  // 把a对象赋给Apple变量,丢失尖括号里的类型信息
 
  Apple b = a;      // ②
 
  // b只知道size的类型是Number
 
  Number size1 = b.getSize();
 
  // 下面代码引起编译错误
 
  Integer size2 = b.getSize();  // ③
 
 }
 
}

Java のジェネリックスの詳細な説明に関連するその他の記事については、PHP 中国語 Web サイトに注目してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。