>  기사  >  Java  >  자바 제네릭

자바 제네릭

黄舟
黄舟원래의
2017-02-24 09:56:331149검색

1. 제네릭 소개

제네릭은 Java SE 1.5의 새로운 기능입니다. 매개변수화된 유형 즉, 작업 중인 데이터 유형이 매개변수로 지정됩니다.
Java SE 1.5 이전에는 제네릭이 없을 때 "임의" 매개변수가 객체 유형을 참조하여 구현되었습니다. "임의"의 단점은 명시적인 유형 변환 이 필요하다는 점이었습니다. 변환을 수행하려면 개발자가 실제 매개변수 유형을 미리 알아야 합니다. 강제 유형 변환 오류의 경우 컴파일러는 오류 메시지를 표시하지 않을 수 있으며 실행될 때만 예외가 발생합니다. 이는 보안상 위험합니다.
 제네릭의 장점은 컴파일 타임에 유형 안전성이 확인되고 코드 재사용을 개선하기 위해 모든 캐스트가 자동으로 암시적으로 수행된다는 것입니다.

2. 제네릭이 필요한 이유는 무엇인가요?

2.1. 컴파일 시 데이터 유형 감지

먼저 ArrayList에 문자열이 추가되는 경우를 살펴보겠습니다. 실수로” "다음 코드와 같이 정수가 추가되었습니다.
오류가 없습니다.

자바 제네릭

그러나 실행하면 오류가 보고됩니다. " java.lang.classCastException"

자바 제네릭

ArrayList는 객체 배열 private transient Object[] elementData;
을 유지하기 때문에 get()을 사용하면 이 필요한 Object 객체를 반환합니다. 🎜> 을 캐스팅했는데 정수 값이 중간에 섞여서 강제 변환이 실패합니다. 이 오류는 Object의 임의
로 인해 발생합니다. 컴파일 단계에서 데이터 유형 오류가 발견되면 매우 편리할 것입니다. 제네릭
은 다음 요구 사항을 충족합니다. ArrayList는 제네릭을 사용하여 수정합니다. 컴파일 중에 찾을 수 있습니다. 단계. 오류가 보고되었습니다. 자바 제네릭

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. 심층 제네릭

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를 사용할 수 있습니다. 멤버 변수로 사용되므로 코드는 universal

입니다. 리팩토링된 코드는 다음과 같습니다.

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());
    }

실행 결과: 자바 제네릭

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());
}

위의 일반 구문을 설명하겠습니다.


유형 홀더를 나타내는 데 사용 이름은 a와 동일합니다. 형식 매개변수는 전달된 실제 데이터의 유형에 따라 데이터 유형이 결정되며, 멤버, 매개변수 및 메소드의 반환 값 유형으로 T가 사용됩니다.
T는 이름일 뿐이고 임의로 선택하셔도 됩니다.
GenDemo 클래스, T는 어떠한 제한도 두지 않으며 실제로 Object와 동일합니다.
GenDemo 클래스와 동일합니다.

객체와 비교하여 제네릭을 사용하여 정의된 클래스는 정의 및 선언에서 실제 데이터 유형을 지정하기 위해 다음과 같이 사용할 수 있습니다.

GenDemo dOD = new GenDemo
(new Double(23)); 지정하지 않은 채로 둘 수도 있으며 강제 변환이 필요합니다.

아래에서는 Java의 제네릭
제한된 제네릭
다중 인터페이스 제한
와일드카드 제네릭

에 대해 계속 설명합니다.

一、泛型简介

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

二、为什么需要泛型

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

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

자바 제네릭

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

자바 제네릭

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

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());
    }

运行结果:
자바 제네릭

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으로 문의하세요.