java--泛型

在jdk1.5之前,集合中可以装多种类型不同的元素,由于一些原因,在jdk1.5推出了泛型,用于在编译阶段限制放入集合中的元素为同一类型。对于没有使用泛型的集合又称为原始类型泛型。

// 原始类型集合
ArrayList arr = new ArrayList();
arr.add("abc");
arr.add(123);
arr.add(false);

// 使用泛型的集合
ArrayList<String> arr0 = new ArrayList<>();
arr0.add("abc");
arr0.add(123);  // 错误,只能添加String类型的元素

为什么说泛型是只对编译器器作用的呢?因为泛型会在编译的使用其约束作用,但是在生成字节码文件的时候会将泛型去掉。所以可以在程序运行的时候通过反射向集合中添加其他类型的元素。

        // 通过反射向集合中写入不同于参数类型的值
        ArrayList<Integer> str = new ArrayList<Integer>();
//      str.add("bb");  // 直接加可定是不行的,会有泛型的限制
        str.getClass().getMethod("add", Object.class).invoke(str, "abc");
        System.out.println( str.get(0));

        // 因为编译器在编译之后就把泛型去掉了,所以两个对应的是同一个class
        Collection<String> col1 = new ArrayList<>();
        System.out.println(col1.getClass() == str.getClass());

另外,不能因为jdk1.5有了泛型,就导致jdk1.5之前的程序不能使用,所以1.5的泛型是可以兼容之前的程序的。

        ArrayList arr1 = new ArrayList();  // 正确
        ArrayList arr2 = new ArrayList<String>();  // 正确
        arr2.add(23);   
        arr2.add(false); 
        // arr2之所以可以添加其他类型的值,因为arr2的本质是一个原始类型的集合,虽然将String泛型的集合赋值给了arr2,但是arr2还是原始类型的集合。

        ArrayList<String> arr3 = new ArrayList(); // 正确 
        ArrayList<String> arr31 = arr2; // 正确 
        // 原始类型集合可以存放多种类型的值,但是也是可以将他赋值arr3,因为为了兼容1.5之前的程序嘛

        ArrayList<Integer> arr4 = arr2; // 正确
        // 将arr2赋值给arr4,,没有问题。为了兼容1.5之前写的代码嘛。但是你在这之后添加的元素类型就得是Integer类型的
//      arr4.add("23");
        System.out.println(arr4.get(0));
        System.out.println(arr4.get(1));

        // 在泛型中,是没有继承的关系的
//      ArrayList<String> arr4 = new ArrayList<Object>(); // 错误的
//      ArrayList<Object> arr4 = new ArrayList<String>(); // 错误的

在使用泛型的时候,可以使用?表示泛型,也可以对?表示的泛型做一些限制。

        ArrayList arr = new ArrayList();
        arr.add("xxx");
        arr.add(56);
        printCollection(arr);

        // Collection<?> 表示在调用方法的时候传递任意类型的泛型
        public static void printCollection(Collection<?> collection){
            // 在使用泛型通配符时,不能使用与泛型参数类型有关的方法。毕竟在运行时不能确定泛型的参数类型。
        //  collection.add("aa"); 
            for(Object obj : collection){
                System.out.println(obj);
            }

        }

使用extends或super对泛型类型做一些限制。

//      exrends 这个表示?接受的泛型类型必须是继承自Number类的,有称为上边界
//      ArrayList<? extends Number> a1 = new ArrayList<String>(); //  错误
        ArrayList<? extends Number> a1 = new ArrayList<Integer>(); //  正确

        // super 表示?接受的泛型类型必须是Number类的父类,又称为上边界
        ArrayList<? super Number> a2 = new ArrayList<Object>();   // 正确
//      ArrayList<? super Number> a2 = new ArrayList<Integer>();  // 错误

另外,你也可以在方法中自定义泛型。定义的泛型要写在返回值前面,并且要使用<>括起来你的泛型。这个写法是java模仿C++的写法的。但是要注意,自定义的泛型的传入值不能是基本类型的引用。

        swap(new String[]{"abc","123","sss"}, 0, 1);
//      swap(new int[]{2,3,4}, 1, 0); 
//      报错,使用自定义泛型时,自定义的泛型的传入值不能是基本类型的引用
        swap(new Integer[]{2,3,4}, 1, 0); // 正确


        // 自定义泛型, 可以对泛型做一些限制
        // <T extends Object> 写在 返回值前面的,表示声明了一个类型。模仿C++的写法。
        public static <T extends Object> void swap(T[] arr, int x, int y){
            T tmp = arr[x];
            arr[x] = arr[y];
            arr[y] = tmp;
        }

那么自定义泛型的用途有哪些呢?先来看一个简单的例子。


/*
 * 假如有这样的需求:在添加的时候可以添加任意类型的数值,查找时返回其添加的类型
 * 那么我就可能会在add方法上使用自定义泛型,这样就可以添加任意的类型了。
 * 在find方法上也使用自定义泛型。不就行了嘛。可是真的行吗?
 */

public class GeneralDao {


    public <T> void add(T obj){
        // doSomething  
    }


    public <T> T findById(int id){
        // doSomething
        return null;
    }

}

使用时:

    GeneralDao dao = new GeneralDao();
    dao.add("Str");   //添加String类型
    int a = dao.findById(0);  
    // 查找返回值类型是泛型,那岂不是可以赋值给任何类型嘛?
    // 可是你添加什么类型,查找返回值就赋值给你添加的类型不就行了吗。
    // 那么如果,添加和查找不是同一个人写的呢?

对于这种,涉及到类中有多个方法使用到泛型的问题,那么泛型就该使用类级别的泛型。这样才能起到添加什么类型就返回什么类型的要求。
使用类级别的泛型:


// 类级别的泛型使用,和jdk帮助文档中形式一样。
// 这样在创建对象的时候,就得指定T的类型,就会有,添加什么,返回什么的效果。
// 使用了类级别的泛型就不用再写方法级别的泛型了
public class GeneralDao<T> {

    public /*<T>*/ void add(T obj){

    }

    public /*<T>*/ T findById(int id){
        return null;
    }

}

使用类级别的泛型:

        GeneralDao<String> dao = new GeneralDao<String>();
        dao.add("Str");
        String s = dao.findById(0);
        // 在查找的时,返回的类型就只能是你添加的类型。编译器会给出你添加的类型
        // 这个就有点像java api给我们提供使用的集和泛型编译限制了

最后一个,如果你想要知道泛型的实际类型可以使用反射得到。

//      可以通过使用方法来获得泛型的实际参数类型。
        Method m = Generictiy.class.getMethod("applyGeneral", Vector.class);
        System.out.println(m.getGenericParameterTypes()[0]);

        public void applyGeneral(Vector<Date> v){

        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: jmu-java-05集合()-10-generalstack是关于Java中的通用栈的学习内容。通用栈是一种可以存储任意类数据的栈结构,通过的方式实现。在学习中,我们可以了解到通用栈的实现原理、使用方法以及注意事项等内容,帮助我们更好地理解和应用Java。 ### 回答2: JMU-Java-05集合()-10-GeneralStack是一个Stack(栈)的实现类,使用Java中的进行定义,可以存储任何类的数据。 在该类中,使用一个Object类的数组进行存储元素,并通过一个整变量top来表示当前栈顶元素的下标。在push(入栈)方法中,先将top加一,然后将元素存储在数组中,实现了入栈的功能。在pop(出栈)方法中,先判断栈是否为空,若为空则抛出栈空异常,否则将top减一,并返回数组中相应的元素,实现了出栈的功能。其他方法如isEmpty(判断栈是否为空)、isFull(判断栈是否已满)和size(获取栈中元素个数)也在该类中实现。 该类的定义使得我们可以使用该类存储任何类的数据,而不需要在定义类时指定数据类,提高了其灵活性和可复用性。在使用时,我们只需要在创建对象时传入相应的数据类,如:GeneralStack<Integer> stack = new GeneralStack<Integer>();即可。 该类还实现了Iterable接口,使得该类可以使用foreach循环进行遍历操作,方便了我们对栈中元素的操作。同时,该类还通过对数组的动态扩容,解决了数组固定大小的限制问题,从而提高了该栈类的通用性和易用性。 总之,JMU-Java-05集合()-10-GeneralStack是一个使用实现的通用栈类,可以存储各种类的数据,并提供了常用的栈操作方法,具有较高的可复用性与适用性。 ### 回答3: jmu-java-05集合()-10-generalstack,意为基于的栈实现。 首先我们需要了解什么是是Java SE 5引入的一个新特性,它可以让我们在编写代码时定义一些未知的类参数,以达到代码的复用和类安全的目的。对于集合类或者其他容器类而言,适用于任何类的容器的需求是普遍存在的,这时就可以应用。 在Java中使用<>标识,其语法格式如下: ```java public class 类名<类参数列表> { //成员变量、方法等 } ``` 类参数列表是由逗号隔开的参数列表,可以理解为未知类的占位符。这样的好处是可以在编写集合类时,避免出现类转换等一系列问题,提高程序的可读性。 而在jmu-java-05集合()-10-generalstack中,则是基于实现的数据结构——栈。栈是一种后进先出(LIFO)的数据结构,它只允许在栈顶添加或删除元素,因此操作非常简单、快速并且高效。 在该实现中,栈的元素可以是任意类,数据元素入栈则是通过 push() 方法实现的,出栈则是通过 pop() 方法实现的。其中,push() 方法用于向栈中添加新的元素,pop() 方法用于弹出并返回栈顶的元素。同时,还提供了 isEmpty() 方法、size() 方法等基本的栈操作方法。 总之,jmu-java-05集合()-10-generalstack是一种基于实现的栈,提供了复用和类安全的目的。其实现使用简便、高效、易于扩展等优点,广应用在Java开发中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值