ホームページ  >  記事  >  Java  >  Javaジェネリックス

Javaジェネリックス

黄舟
黄舟オリジナル
2017-02-24 09:56:331081ブラウズ

1. ジェネリックの概要

ジェネリックは Java SE 1.5 の新機能です。ジェネリックの本質は パラメータ化された型 であり、操作対象のデータ型がパラメータとして指定されることを意味します。
Java SE 1.5 より前では、ジェネリックスが存在しないため、「任意」パラメータは型オブジェクトを参照することによって実現されていました。「任意」の欠点は、明示的な 強制的な型変換 が必要であることでした。パラメータの種類を事前に設定してください。 強制型変換エラーの場合、コンパイラはエラーを表示しない可能性があり、例外はの実行中にのみ発生します。これはセキュリティリスクです。
ジェネリックの利点は、コンパイル時に型安全性がチェックされ、すべてのキャストが自動的かつ暗黙的に行われるため、コードの再利用が向上することです。

2. ジェネリックが必要な理由

2.1. コンパイル時にデータ型を検出する

まず、次のコードのように、文字列が ArrayList に追加され、整数が「誤って」追加された場合を見ていきます。
エラーはありません:

Javaジェネリックス

しかし、実行されると、エラーが報告されます: "java.lang.classCastException"

Javaジェネリックス

ArrayList は Object 配列を維持するため、private transient Object[] elementData; code> <code>private transient Object[] elementData;
, 使用get()返回的是一个Object对象, 需要强制转换,但是中间混杂一个Integer数值, 导致强制转换失败。这个错误就是由于Object的任意化导致的。
如果能在编译阶段就发现数据类型有错, 那么就很方便,泛型就满足了这个要求:我将这个程序修改一下,ArrayList使用泛型:会发现编译阶段就报错了.
Javaジェネリックス

2.2强制转换是自动的

不使用泛型:

package com.chb.fanxing;public class NoGen {    
private Object ob;    
public NoGen(Object ob) {        
this.ob = ob;
    }
    getter setter...    
    private void showType() {
        System.out.println("数据的实际类型是:" + ob.getClass().getName());
    }    public static void main(String[] args) {
        NoGen ngInt = new NoGen(88);
        ngInt.showType();        int i = (int)ngInt.getOb();
        System.out.println("value = " + i);
        System.out.println("---------------");

        NoGen ngStr = new NoGen("88");
        ngStr.showType();
        String str = (String)ngStr.getOb();
        System.out.println("value = " + str);   
    }
}

使用泛型:

package com.chb.fanxing;public class Gen<T> {    
private T ob;    
public Gen(T ob) {        
this.ob = ob;
    }
    getter setter...    
    private void showType() {
        System.out.println("T的实际类型:"+ob.getClass().getName());
    }    public static void main(String[] args) {        //定义一个Integer版本
        Gen<Integer> genInt = new Gen<Integer>(88);
        genInt.showType();        int i = genInt.getOb();//此处不用强制转换
        System.out.println("value = " + i);
        System.out.println("----------------------");

        Gen<String> genStr = new Gen<String>("88");
        genStr.showType();
        String str = genStr.getOb();
        System.out.println("value = "+str); 
    }
}

运行结果:

两个例子的运行结果是一致的

数据的实际类型是:java.lang.Integervalue = 88
---------------数据的实际类型是:java.lang.String
value = 88

对比两个例子会发现:

  • 使用泛型,强制转换时自动进行的:

int i = genInt.getOb();//此处不用强制转换
  • 而不使用泛型,必须要进行手动强制转化

int i = (int)ngInt.getOb();

三、深入泛型

3.1 、有两个类,我们需要打印他们的成员变量

class StringDemo {    
private String s;    
public StringDemo (String s) {        
this.s = s;
    }
    setter geter....
}
class DoubleDemo{    
private Double d;    
public DoubleDemo(Double d) {        
this.d = d;
    }
    setter getter...
}

3.2、重构

仔细观察两个类功能基本一致,只是数据类型不一样,考虑到重构,因为Object是所有类的基类,所以可以使用Object作为成员变量,这样代码就可以通用了。重构代码如下:

class ObjectDemo{
    private Object ob;    
    public ObjectDemo(Object ob){        
    this.ob = ob;
    }
    setter getter...
}

ObjectDemo测试:

public static void ObjectDemoTest(){
        ObjectDemo strOD = new ObjectDemo("123");
        ObjectDemo dOD = new ObjectDemo(new Double(23));
        ObjectDemo od = new ObjectDemo(new Object());
        System.out.println((String)strOD.getOb());
        System.out.println((Double)dOD.getOb());
        System.out.println(od.getOb());
    }

运行结果:
Javaジェネリックス

3.3使用泛型重构

发现上面的ObjectDemoTest()、get() を使用すると 強制変換 が必要な Object オブジェクトが返されますが、途中に整数値が混在しているため、強制変換が失敗します。このエラーは、オブジェクトの恣意化が原因で発生します。

コンパイル段階でデータ型エラーが見つかると、非常に便利です。

ジェネリック はこの要件を満たします: このプログラムを変更し、ArrayList にジェネリックを使用します: コンパイル段階でエラーが報告されることがわかります。 .

2.2強制変換は自動です

ジェネリックスを使用しない:

class GenDemo<T>{    
private T t;    
public GenDemo(T t) {        
this.t = t;
    } 
    public void setT(T t) {        
    this.t = t;
    }    
    public T getT() {        
    return t;
    }
}

ジェネリックスを使用する:

public static void GenTest() {
        GenDemo<String> strOD = new GenDemo<String>("123");
        GenDemo<Double> dOD = new GenDemo<Double>(new Double(23));
        GenDemo<Object> od = new GenDemo<Object>(new Object());
        System.out.println(strOD.getT());
        System.out.println(dOD.getT());
        System.out.println(od.getT());
}

実行結果: 2 つの例の実行結果は一貫しています

package com.chb.fanxing;public class NoGen {    
private Object ob;    
public NoGen(Object ob) {        
this.ob = ob;
    }
    getter setter...    private void showType() {
        System.out.println("数据的实际类型是:" + ob.getClass().getName());
    }    public static void main(String[] args) {
        NoGen ngInt = new NoGen(88);
        ngInt.showType();        int i = (int)ngInt.getOb();
        System.out.println("value = " + i);
        System.out.println("---------------");

        NoGen ngStr = new NoGen("88");
        ngStr.showType();
        String str = (String)ngStr.getOb();
        System.out.println("value = " + str);   
    }
}

2 つの例を比較すると、次のことがわかります:

    ジェネリックを使用すると、強制変換が自動的に実行されます:

package com.chb.fanxing;public class Gen<T> {    
private T ob;    
public Gen(T ob) {        
this.ob = ob;
    }
    getter setter...    private void showType() {
        System.out.println("T的实际类型:"+ob.getClass().getName());
    }    public static void main(String[] args) {        //定义一个Integer版本
        Gen<Integer> genInt = new Gen<Integer>(88);
        genInt.showType();        int i = genInt.getOb();//此处不用强制转换
        System.out.println("value = " + i);
        System.out.println("----------------------");

        Gen<String> genStr = new Gen<String>("88");
        genStr.showType();
        String str = genStr.getOb();
        System.out.println("value = "+str); 
    }
}

  • ジェネリックを使用しない場合、手動で行う必要があります。変換
数据的实际类型是:java.lang.Integervalue = 88
---------------数据的实际类型是:java.lang.String
value = 88
3. 詳細なジェネリック🎜🎜3.1. 2 つのクラスがあり、そのメンバー変数を出力する必要があります🎜
int i = genInt.getOb();//此处不用强制转换
🎜3. 2 つの基本的な関数を注意深く観察します。クラスは同じですが、データ型が異なります。リファクタリングを考慮すると、Object はすべてのクラスの基本クラスであるため、Object をメンバー変数として使用して、コードを それは世界共通です 🎜。リファクタリングされたコードは次のとおりです: 🎜
int i = (int)ngInt.getOb();
🎜ObjectDemo テスト: 🎜
class StringDemo {    
private String s;    
public StringDemo (String s) {        
this.s = s;
    }
    setter geter....
}
class DoubleDemo{    
private Double d;    
public DoubleDemo(Double d) {        
this.d = d;
    }
    setter getter...
}
🎜実行結果: 🎜Javaジェネリックス🎜🎜3.3 汎用リファクタリングの使用🎜🎜 上記の ObjectDemoTest() では 🎜強制変換🎜 を使用する必要があることがわかりました。面倒です🎜 正しい変換を行うためには、変換するデータ型も事前に知っておく必要があります。そうしないと、ビジネスをコンパイルするときは問題ありませんが、実行すると「classCastException」が発生します。したがって、キャストを自分で行う必要はありません。これはジェネリックにとって特に重要です。 🎜
class ObjectDemo{
    private Object ob;    
    public ObjectDemo(Object ob){        
    this.ob = ob;
    }
    setter getter...
}
🎜 テスト: 🎜 手動キャストを排除します 🎜🎜
public static void ObjectDemoTest(){
        ObjectDemo strOD = new ObjectDemo("123");
        ObjectDemo dOD = new ObjectDemo(new Double(23));
        ObjectDemo od = new ObjectDemo(new Object());
        System.out.println((String)strOD.getOb());
        System.out.println((Double)dOD.getOb());
        System.out.println(od.getOb());
    }
🎜 上記の一般的な構文を説明しましょう: 🎜🎜 データの型が実際に渡される、入力の型。データが決定され、T がメソッドのメンバー、パラメーター、および戻り値の型として使用されます。 🎜T は単なる名前です。自由に選択できます。 🎜クラス GenDemo、T には制限はありません。実際には Object と同等であり、🎜 クラス GenDemo と同等です。 🎜 Object と比較して、ジェネリックスを使用して定義されたクラスは、定義および宣言中に を使用して実際のデータ型を指定できます。 🎜🎜GenDemo🎜🎜 dOD = new GenDemo🎜 🎜( new Double(23)); 🎜 を指定しないままにすることもでき、その場合は強制変換が必要です。 🎜🎜以下で Java ジェネリックを続けます 🎜制限付きジェネリック 🎜複数のインターフェイス制限 🎜ワイルドカード ジェネリック 🎜

一、泛型简介

  泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
  在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
  泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。

二、为什么需要泛型

2.1、编译期对数据类型进行检测

首先我们看一个案例,向一个ArrayList中添加字符串,“不小心”添加了整数,如下面代码,
并没有错误:

Javaジェネリックス

但是执行时,会报错:“java.lang.classCastException”

Javaジェネリックス

因为ArrayList中维护的是一个Object数组, private transient Object[] elementData;
, 使用get()返回的是一个Object对象, 需要强制转换,但是中间混杂一个Integer数值, 导致强制转换失败。这个错误就是由于Object的任意化导致的。
如果能在编译阶段就发现数据类型有错, 那么就很方便,泛型就满足了这个要求:我将这个程序修改一下,ArrayList使用泛型:会发现编译阶段就报错了.
Javaジェネリックス

2.2强制转换是自动的

不使用泛型:

package com.chb.fanxing;public class NoGen {    
private Object ob;    
public NoGen(Object ob) {        
this.ob = ob;
    }
    getter setter...    private void showType() {
        System.out.println("数据的实际类型是:" + ob.getClass().getName());
    }    public static void main(String[] args) {
        NoGen ngInt = new NoGen(88);
        ngInt.showType();        int i = (int)ngInt.getOb();
        System.out.println("value = " + i);
        System.out.println("---------------");

        NoGen ngStr = new NoGen("88");
        ngStr.showType();
        String str = (String)ngStr.getOb();
        System.out.println("value = " + str);   
    }
}

使用泛型:

package com.chb.fanxing;public class Gen<T> {    
private T ob;    
public Gen(T ob) {        
this.ob = ob;
    }
    getter setter...    private void showType() {
        System.out.println("T的实际类型:"+ob.getClass().getName());
    }    public static void main(String[] args) {        //定义一个Integer版本
        Gen<Integer> genInt = new Gen<Integer>(88);
        genInt.showType();        int i = genInt.getOb();//此处不用强制转换
        System.out.println("value = " + i);
        System.out.println("----------------------");

        Gen<String> genStr = new Gen<String>("88");
        genStr.showType();
        String str = genStr.getOb();
        System.out.println("value = "+str); 
    }
}

运行结果:

两个例子的运行结果是一致的

数据的实际类型是:java.lang.Integervalue = 88
---------------数据的实际类型是:java.lang.String
value = 88

对比两个例子会发现:

  • 使用泛型,强制转换时自动进行的:

int i = genInt.getOb();//此处不用强制转换
  • 而不使用泛型,必须要进行手动强制转化

int i = (int)ngInt.getOb();

三、深入泛型

3.1 、有两个类,我们需要打印他们的成员变量

class StringDemo {    
private String s;    
public StringDemo (String s) {        
this.s = s;
    }
    setter geter....
}
class DoubleDemo{    
private Double d;    
public DoubleDemo(Double d) {        
this.d = d;
    }
    setter getter...
}

3.2、重构

仔细观察两个类功能基本一致,只是数据类型不一样,考虑到重构,因为Object是所有类的基类,所以可以使用Object作为成员变量,这样代码就可以通用了。重构代码如下:

class ObjectDemo{
    private Object ob;    
    public ObjectDemo(Object ob){        
    this.ob = ob;
    }
    setter getter...
}

ObjectDemo测试:

public static void ObjectDemoTest(){
        ObjectDemo strOD = new ObjectDemo("123");
        ObjectDemo dOD = new ObjectDemo(new Double(23));
        ObjectDemo od = new ObjectDemo(new Object());
        System.out.println((String)strOD.getOb());
        System.out.println((Double)dOD.getOb());
        System.out.println(od.getOb());
    }

运行结果:
Javaジェネリックス

3.3使用泛型重构

发现上面的ObjectDemoTest() 中必须要使用强制转换,这比较麻烦,我们还必须事先知道要转换的数据类型,才能进行正确的转换,否则,会出现错误, 业务编译时没有问题,但是一运行,会出现”classCastException”。所以我们需要不用自己进行强制转换,这是泛型就尤为重要。

class GenDemo<T>{    private T t;    public GenDemo(T t) {        this.t = t;
    } 
    public void setT(T t) {        this.t = t;
    }    public T getT() {        return t;
    }
}

测试:省去了手动进行强制转换

public static void GenTest() {
        GenDemo<String> strOD = new GenDemo<String>("123");
        GenDemo<Double> dOD = new GenDemo<Double>(new Double(23));
        GenDemo<Object> od = new GenDemo<Object>(new Object());
        System.out.println(strOD.getT());
        System.out.println(dOD.getT());
        System.out.println(od.getT());
}

下面解释一下上面的泛型语法:

使用表示一个类型持有者名称, 相当于一个形参,数据的类型是有实际传入的数据的类型决定,然后T作为成员、参数、方法的返回值的类型。
T仅仅是一个名字,可以随意取的。
class GenDemo , T没有进行任何限制, 实际相当于 Object,  
等同于 class GenDemo。
与Object相比,使用泛型所定义的类,在定义和声明,可以使用来制定真实的数据类型,如:

GenDemo dOD = new GenDemo(new Double(23));
也可以不指定,那么就需要进行强制转换。

 以上就是java之泛型的内容,更多相关内容请关注PHP中文网(www.php.cn)!


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