首页 > 技术文章 > java泛型

bclshuai 2019-03-25 14:36 原文

1         泛型

1.1            泛型的作用

泛型有两种作用,一种是让接口或方式更通用,一种是限制作用。

第一种是一种是让接口或方式更通用,泛型和C++的模板很相似,有时在定义函数或者接口时,不确定需要传入的参数是什么类型,这时可能要创建多个重载函数来枚举所有的数据类型。而有了泛型之后,直接用一个标识符代替具体的数据类型,在调用的时候传入具体的数据类型给标识符。从而实现接收任意数据类型的方法。例如:

public interface Generator<T> {

    public T next();

}

第二则是限制作用。例如创建List时是以Object作为基类,所以可以放入任何类型的对象List arrayList = new ArrayList();arrayList.add("aaaa");arrayList.add(100);但是在输出的时候,不知道输出是什么类型。使用不当会导致奔溃。所以需要通过泛型来限制输入数据的类型。List<String> arrayList = new ArrayList<String>();只能输入String类型的数据,其他类型数据则会报错。

1.2            泛型的使用

1.2.1             泛型类

泛型类的定义

public class Generic<T>{

    //key这个成员变量的类型为T,T的类型由外部指定 

    private T key;

    public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定

        this.key = key;

    }

    public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定

        return key;

    }

}

泛型对象的创建

//泛型的类型参数只能是类类型(包括自定义类),不能是简单类型

//传入的实参类型需与泛型的类型参数类型相同,即为Integer.

Generic<Integer> genericInteger = new Generic<Integer>(123456);

Log.d("泛型测试","key is " + genericInteger.getKey());

也可以省略泛型形参,会根据输入的数据类型自动识别。

Generic generic2 = new Generic(55.55);

Generic generic3 = new Generic(false);

 

1.2.2             泛型接口

定义泛型接口:

public interface Generator<T> {

    public T next();

}

实现泛型接口

(1)不指定泛型类型,实现类中也要加入泛型标识

class FruitGenerator<T> implements Generator<T>{

    @Override

    public T next() {

        return null;

    }

}

(2)指定泛型类型,实现类中无需泛型标识

public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override

    public String next() {

        Random rand = new Random();

        return fruits[rand.nextInt(3)];

    }

}

1.2.3             泛型方法

 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,

    public <T> T showKeyName(Generic<T> container){

        System.out.println("container key :" + container.getKey());

        //当然这个例子举的不太合适,只是为了说明泛型方法的特性。

        T test = container.getKey();

        return test;

    }

在泛型类中者形参中带有泛型标识符public void showKeyValue1(T key) ;showKeyValue1(Generic<Number> obj);带有通配符的函数 public void showKeyValue2(Generic<?> obj);或返回值是泛型标识符public T getKey();这些都不算是真正的泛型方法。其中的泛型标识符是类的泛型标识符,创建对象时传入实际的数据类型,调用泛型方法时,必须传入创建对象时的泛型数据类型。泛型类中泛型方法声明泛型标识符T可以与泛型类的泛型标识符T一样,也可以换成E,或者其他标识符。泛型方法中的标识符在泛型方法调用时,传入实际的数据类型,与泛型类中的标识符T没有必然联系,例如泛型类传入的类型是String,并不代表泛型方法中的T也是String类型。而是在泛型方法调用时传入具体的数据类型。总之泛型标识符只是用来标识,由传入数据类型决定。

1.2.4             泛型可变参数

泛型也可以用于可变参数:

public <T> void printMsg( T... args){

    for(T t : args){

        Log.d("泛型测试","t is " + t);

    }

}

printMsg("111",222,"aaaa","2323.4");//传入不同类型的变量。

1.2.5             静态方法与泛型

因为静态方法属于类,而不属于对象,所有无法使用创建对象传入泛型的数据类型。所以静态方法如果要用泛型的话,只能定义为泛型方法。在静态方法调用时,传入具体的数据类型。

public class StaticGenerator<T> {

   //public static void show(T t){..};ERROR此时编译器会提示错误信息:

public static <T> void show(T t){ }};

1.2.6             泛型通配符?

Ingeter是Number的一个子类,Ingeter变量可以传给Number。但是Generic<Ingeter>变量却不可以传给Generic<Number>,不是子类和父类关系。这样就无法传递,因此类型通配符云而生。

public void showKeyValue1(Generic<?> obj);

使用?代替具体的类型实参,可以把?看成所有类型的父类。任何类型的Generic<Number>、Generic<String>、Generic<Integer>等都可以传入,Generic<?>各种泛型的父类,但是只能作为泛型形参,不能创建对象保存数据。可以解决不确定具体类型时,直接用?号代替;但同时也引入另外一个问题,因为不确定是什么类型,所在方法中只使用Object类中的基本功能方法。不可以用传入类型的特殊的方法。

1.2.7             泛型的边界

泛型的作用是能够传入任意数据类型,但是有时需要限制输入的类型范围,例如只允许number的子类传入。这时就需要设置边界;

public void showKeyValue1(Generic<? extends Number> obj);

这样只有Number的子类构成的变量Generic<Integer>、Generic<Float>等才可以传人,而Generic<String>则不能传入。这就是边界。

泛型类也可以设置边界:

public class Generic<T extends Number>{};

泛型方法也可以设置边界:

public <T extends Number> T showKeyName(Generic<T> container){};

 

1.2.8             泛型数组

Java是不支持泛型数组的,也就是说下面的这个例子是不可以的:

List<String>[] ls = new ArrayList<String>[10]; 

那么为什么不可以呢?来看一个例子:

List<String>[] lsa = new List<String>[10]; // 实际是不可行,先假设可以这样.

Object o = lsa;//创建一个object引用指向数组

Object[] oa = (Object[]) o;

List<Integer> li = new ArrayList<Integer>();//新建一个Integer的 List

li.add(new Integer(3));//加入一个数据

oa[1] = li; // 将list赋值给数组的第一个元素

String s = lsa[1].get(0); //这里会报异常

由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给oa[1]赋上一个ArrayList而不会出现异常,但是在取出数据的时候却要做一次类型转换,所以就会出现ClassCastException,如果可以进行泛型数组的声明,上面说的这种情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。所以无法创建指定类型的泛型数组,可以采用通配符的方式创建数组:

List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.   

Object o = lsa;   

Object[] oa = (Object[]) o;   

List<Integer> li = new ArrayList<Integer>();   

li.add(new Integer(3));   

oa[1] = li; // Correct.   

Integer i = (Integer) lsa[1].get(0); // OK

因为使用了通配符,所以获取数据后需要进行强制类型转换,就不会出错。

 

自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:

https://www.cnblogs.com/bclshuai/p/11380657.html

百度云盘下载地址:

链接:https://pan.baidu.com/s/1swkQzCIKI3g3ObcebgpIDg

提取码:mc8l

微信公众号获取最新的软件和视频介绍

QStockView

推荐阅读