目次
1. 包装
Java では、基本データ型は Object から継承されません。ジェネリック コードで基本データ型をサポートするために、Java には基本データ型ごとにラッパー型があります。
1.1 基本的なデータ型と対応するパッケージング クラス
基本的なデータ型 | 包装 |
バイト | バイト |
短い | 短い |
整数 | 整数 |
長さ | 長さ |
浮く | 浮く |
ダブル | ダブル |
文字 | キャラクター |
ブール値 | ブール値 |
Integer と Character を除き、他の基本データ型のラッパー クラスの最初の文字は大文字になります。
1.2 梱包と開梱
int i=10; //ボックス化----整数型の新しいオブジェクトを作成し、i の値をオブジェクトのプロパティに入力します。 Integer i1=Integer.valueOf(i); Integer i2=new Integer(i) ; //アンボックス化----Integer オブジェクトから値を取り出し、それを基本データ型 int I=i1.intValue();
1.3 自動梱包と自動開梱
使用中にボックス化とボックス化解除を行うと多くのコードを記述することになりますが、Java では開発者の負担を軽減するために、自動ボックス化と自動アンボックス化という自動機構が用意されています。
int i=5; //自動ボックス化 Integer i1=i; Integer i2=(Integer) i; //自動アンボックス化 int i3=i1; int i4=(int) i1;
インタビューの質問: 次のコードの出力は何ですか? なぜ?
public static void main(String[] args) { 整数 a=127; 整数 b=127; 整数 c=128; 整数 d=128; System.out.println(a==b); System.out.println(c==d); }
まず、Integer はラッパー クラスであり、== は 2 つのオペランドが格納されているアドレスを比較します。
次に、Integer の範囲が -128~127 の場合、value は配列の添字に対応する値を返します。それ以外の場合は、新しい Integer オブジェクトが作成されます。したがって、a と b に 127 が格納されている場合は、同じアドレスを参照する新しいオブジェクトは作成されませんが、c と d に 128 が格納されている場合は、異なるアドレスを参照する新しいオブジェクトが作成されます。
2. 一般的な概念と概要
2.1 一般的な概念
一般的なクラスとメソッドは、特定の型 (基本型またはカスタム クラス) のみを使用できます。複数の型に適用できるコードを記述したい場合、この厳格な制限によりコードが大幅に制限されるため、JDK1.5 では新しい構文 (ジェネリックス: 多くの型に適用可能) が導入されました。コードの観点からは、実装された型パラメータ化。
2.2 一般的な導出
配列メンバーを含むクラスを実装すると、あらゆる種類のデータを配列に格納でき、配列内の特定の添え字の値もメンバー メソッドに従って返すことができます。
Object を使用して配列の型を定義し、配列にあらゆる種類のデータを格納できるようにします。
class MyArray{ private Object[] array=new Object[10]; public Object getValue(int pos){ return this.array[pos]; public void setValue(int pos , Object obj){ this.array[pos]=obj; } } public class MyArrayTextDemo { public static void main(String[] args) { MyArray myArray =new MyArray(); myArray.setValue(0,10); myArray.setValue(1,"こんにちは"); String ret=myArray.getValue(1);// システム.out.println(ret); } }
上記のコードを見ると、任意のデータ型を格納できることがわかりますが、添え字に該当するデータを取得する際にはコンパイルエラーが報告され、強制的な型変換が必要となります。
この場合、配列はあらゆるタイプのデータを格納できますが、多くの場合、同時に多数のデータ型を保持するのではなく、1 つのデータ型のみを保持することが望ましいと考えられます。したがって、ジェネリックの主な目的は、現在のコンテナが保持すべきデータのタイプを指定し、コンパイラにそれをチェックさせることです。このとき、型をパラメータとして渡す必要があり、必要な型はすべて渡されます。
文法
class ジェネリッククラス名<型パラメータリスト>{
}
class ClassName<T1,T2,T3,...,Tn>{
}
class ジェネリック クラス名<型パラメータ リスト> extends 継承されたクラス { } class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> { }
上記のコードを改善します。
class MyArray<T>{ private T[] array=(T[]) new Object[10]; //使用できますが推荐 public T getValue(int pos){ return this.array[pos]; public void setValue(int pos , T obj){ this.array[pos]=obj; } }class MyArray<T>{ private Object[] array= new Object[10];//推荐使用 public T getValue(int pos){ return (T) this.array[pos]; public void setValue(int pos , T obj){ this.array[pos]=obj; } }public class MyArrayTextDemo { public static void main(String[] args) { MyArray<Integer> myArray =new MyArray(); myArray.setValue(0,10); myArray.setValue(1,15); int ret= myArray.getValue(1);//不用强制转化 System.out.println(ret); } }
注:クラス名の後の <T> はプレースホルダーを表し、現在のクラスがジェネリック クラスであることを示します。型パラメーターは通常、最初の大文字で表され、一般的に使用される名前は次のとおりです: E—Element、K—Key、V—Value、N—Number、T—Type、S、U、V など。ジェネリック型の配列を新規作成することはできません。
T[] arr = new T[5];//エラーが報告されます
type の後に <Integer> を追加して現在格納されているデータの型を指定すると、コンパイラは格納されている要素の型が正しくないかどうかもチェックします。
3. ジェネリッククラスの使用
文法
ジェネリッククラス <型引数> 変数名; //ジェネリッククラス参照を定義
new ジェネリック クラス <type argument> (コンストラクター引数) //ジェネリック クラス オブジェクトをインスタンス化します。
例
MyArray<Integer> list=new MyArray<Integer>();
ジェネリックはクラスのみを受け入れることができ、すべての基本データ型はラッパー クラスを使用する必要があります。
型推論
コンパイラがコンテキストから型引数を推定できる場合は、型引数の入力を省略できます。
MyArray<Integer> リスト = new MyArray<>();
4. ネイキッドタイプ(わかる)
ネイキッド型は、型パラメーターのないジェネリック クラスです。たとえば、MyArray リストはネイキッド型です。
MyArray list=new MyArray();
注: ネイキッド タイプ自体は使用しないでください。ネイキッド タイプは、古いバージョンの API との互換性のために予約されているメカニズムです。
概要: ジェネリックはデータ型をパラメータ化して転送します。<T> を使用して現在のクラスがジェネリック クラスであることを示します。ジェネリック クラスの利点: データ型のパラメータ化、自動型チェックとコンパイル中の変換。
5. ジェネリックスのコンパイル方法
5.1 消去メカニズム
コマンド javap -c を使用してバイトコード ファイルを表示します。
すべての T が Object であることがわかります。コンパイル プロセス中に、すべての T を Object に置き換えるメカニズムは消去メカニズムと呼ばれます。Javaの汎用メカニズムはコンパイル レベルで実装されます。コンパイラによって生成されたバイトコードには、実行時のジェネリック型情報は含まれません。
5.2 ジェネリック型の配列をインスタンス化できない理由
class MyArray<T> { public T[] array = (T[])new Object[10];
public T getValue(int pos) { return this.array[pos]; public void setValue(int pos,T val) { this.array [pos] = val; public T[ ] getArray() { 配列を返します。public static void main(String[] args) { MyArray<Integer> myArray1 = new MyArray<>(); Integer[] str = myArray1.getArray();//报错}
理由: T を Object に置き換えた後、Object[] が Integer[] 参照に割り当てられます。平たく言うと、返された Object 配列には、任意のデータ型が格納される可能性があり、String の場合もあれば、Person の場合もあります。実行時には、Integer 型の配列に直接転送されます。コンパイラは、それが安全でないとみなします。
6. ジェネリックの上限
ジェネリック クラスを定義する場合、渡される型変数に特定の制約を課す必要がある場合があります。これは型の境界によって制限される可能性があります。
文法
クラス ジェネリック クラス名 <型パラメータは型境界を拡張> { ... }
例
public class MyArray<E extends Number> { ... } // Number のサブタイプのみが E の型引数として受け入れられることを示します
MyArray<Integer> list1; // コンパイルに合格しました、Integer は Number のサブタイプです
MyArray<String> list2; // コンパイル エラー、String は Number のサブタイプではありません
型境界 E が指定されていない場合は、E が Object を拡張したものとみなされます。
複雑な例
public class MyArray<E extends Comparable<E>> { ... } //E は Comparable インターフェイスを実装する必要があります
7. 一般的な方法
文法
アクセス修飾子 <type パラメータリスト> 戻り値 type メソッド名 (パラメータリスト) {
...
}
例1
パブリック クラス テキスト{
public static <E> void swap(E[] array, int i, int j) { E t = array[i]; 配列[i] = 配列[j]; 配列[j] = t; }
}
使用例1:型推論が可能
整数[] a = { ... };
スワップ(a, 0, 9);
文字列[] b = { ... };
スワップ(b, 0, 9);
使用例 2: 型推論を使用しない場合
整数[] a = { ... };
Text.<Integer>swap(a, 0, 9);
文字列[] b={ ... };Text.<String>swep(b,0,9);