泛型

泛型概述

泛型是 JDK1.5 以后增加的,它可以帮助我们建立类型安全的集合。在使用了泛型的集合中,遍历时不必进行强制类型转换。JDK 提供了支持泛型的编译器,将运行时的类型检查提前到了编译时执行,提高了代码可读性和安全性。

泛型的本质就是“数据类型的参数化”,也就是说所操作的数据类型被指定为一个参数。 顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型。这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口。我们可以把“泛型”理解为数据类型的一个占位符(形式参数),即告诉编译器,在调用泛型时必须传入实际类型。

泛型定义格式

  • <类型>:指定一种类型的格式。这里的类型可以看成是形参。
  • <类型1, 类型2, …>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参。
  • 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型。

泛型的好处

  • 把运行时期的问题提前到了编译期间;
  • 避免了强制类型转换

泛型类

  • 格式:

    修饰符 class 类名<类型> {
        类体;
    }
    
  • 示例类:

    public class Generic<T> {
        private T t;
    
        public T getT() {
            return t;
        }
    
        public void setT(T t) {
            this.t = t;
        }
    }
    
  • 测试类:

    public class GenericDemo {
        public static void main(String[] args) {
            Generic<String> g1 = new Generic<String>();
            g1.setT("zjax");
            System.out.println(g1.getT());
    
            Generic<Integer> g2 = new Generic<Integer>();
            g2.setT(30);
            System.out.println(g2.getT());
    
            Generic<Boolean> g3 = new Generic<Boolean>();
            g3.setT(true);
            System.out.println(g3.getT());
        }
    }
    

泛型方法

  • 格式

    修饰符 <类型> 返回值类型 方法名(类型 变量名) {
        方法体
    }
    
  • 示例方法:

    public class Generic {
        public <T> void show(T t) {
            System.out.println(t);
        }
    }
    
  • 测试类:

    public class GenericDemo {
        public static void main(String[] args) {
            Generic g = new Generic();
            g.show("zjax");
            g.show(30);
            g.show(true);
            g.show(12.34);
        }
    }
    

泛型接口

  • 格式

    修饰符 interface 接口名<类型> {
    	接口体;
    }
    
  • 示例接口以及实现类:

    public interface Generic<T> {
        void show(T t);
    }
    
    public class GenericImpl<T> implements Generic<T> {
        @Override
        public void show(T t) {
            System.out.println(t);
        }
    }
    
  • 测试类:

    public class GenericDemo {
        public static void main(String[] args) {
            Generic<String> g1 = new GenericImpl<String>();
            g1.show("zjax");
    
            Generic<Integer> g2 = new GenericImpl<Integer>();
            g2.show(30);
        }
    }
    

类型通配符

**类型通配符的作用:**为了表示各种泛型 List 的父类,可以使用类型通配符

类型通配符的分类:

  • 类型通配符:<?>
    • List<?>:表示元素类型未知的 List,它的元素可以匹配任何的类型。
    • 这种带通配符的 List 仅表示它是各种泛型 List 的父类,并不能把元素添加到其中。
  • 类型通配符上限:<? extends 类型>
    • List<? extends Number>:它表示的类型是 Number 或者其子类型
  • 类型通配符下限:<? super 类型>
    • List<? super Number>:它表示的类型是 Number 或者其父类型

类型通配符的基本使用:

public class GenericDemo {
    public static void main(String[] args) {
        // 类型通配符:<?>
        List<?> list1 = new ArrayList<Object>();
        List<?> list2 = new ArrayList<Number>();
        List<?> list3 = new ArrayList<Integer>();
        System.out.println("--------");

        // 类型通配符上限:<? extends 类型>
        // List<? extends Number> list4 = new ArrayList<Object>(); // error
        List<? extends Number> list5 = new ArrayList<Number>();
        List<? extends Number> list6 = new ArrayList<Integer>();
        System.out.println("--------");

        // 类型通配符下限:<? super 类型>
        List<? super Number> list7 = new ArrayList<Object>();
        List<? super Number> list8 = new ArrayList<Number>();
        // List<? super Number> list9 = new ArrayList<Integer>(); // error
    }
}

自定义泛型

我们可以在类的声明处增加泛型列表,如:<T, E, V>。此处,字符可以是任何标识符,一般采用这3个字母。

class MyCollection<E> {// E:表示泛型;
    Object[] objs = new Object[5];
 
    public E get(int index) {// E:表示泛型;
        return (E) objs[index];
    }
    public void set(int index, E e) {// E:表示泛型;
        objs[index] = e;
    }
}

泛型 E 像一个占位符一样表示“未知的某个数据类型”,我们在真正调用的时候传入这个“数据类型”。

public class TestGenerics {
    public static void main(String[] args) {
        // 这里的”String”就是实际传入的数据类型;
        MyCollection<String> mc = new MyCollection<String>();
        mc.set(0, "aaa");
        mc.set(1, "bbb");
        String str = mc.get(1); // 加了泛型,直接返回 String 类型,不用强制转换;
        System.out.println(str);
    }
}

可变参数

  • 可变参数介绍

    可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了。

  • 可变参数定义格式

    修饰符 返回值类型 方法名(数据类型… 变量名) {
        方法体;
    }
    

    或者:

    修饰符 返回值类型 方法名(数据类型1 变量名, 数据类型2… 变量名) {
        方法体;
    }
    

说明: 当方法中需要使用可变参数时,可变参数一定要放在最后。

示例:

public static int sum(int... a) {
    int sum = 0;
    for(int i : a) {
        sum += i;
    }
    return sum;
}

测试类:

public class ArgsDemo01 {
    public static void main(String[] args) {
        System.out.println(add(1, 2));
        System.out.println(add(1, 2, 3));
        System.out.println(add("zjax", 1, 2, 3));
    }

    public static int add(String info, int... nums) {
        int result = 0;
        System.out.println(info);
        for (int num : nums) {
            result += num;
        }
        return result;
    }

    public static int add(int... a) {
        int result = 0;
        for (int num : nums) {
            result += num;
        }
        return result;
    }
}

可变参数的使用

  • Arrays 工具类中有一个静态方法:

    • public static <T> List<T> asList(T... a):返回由指定数组支持的固定大小的列表

    • 返回的集合不能做增删操作,可以做修改操作

  • JDK9 中,List 接口中有一个静态方法:

    • public static <E> List<E> of(E... elements):返回包含任意数量元素的不可变列表

    • 返回的集合不能做增删改操作

  • JDK9 中,Set 接口中有一个静态方法:

  • public static <E> Set<E> of(E... elements):返回一个包含任意数量元素的不可变集合

  • 在给元素的时候,不能给重复的元素

  • 返回的集合不能做增删操作,没有修改的方法

public class ArgsDemo2 {
    public static void main(String[] args) {
        // test01();
        // test02();
        test03();
    }

    /**
     * public static <T> List<T> asList(T... a):返回由指定数组支持的固定大小的列表
     */
    public static void test01() {
        List<String> list = Arrays.asList("hello", "world", "zjax");
        // list.add("izj"); // java.lang.UnsupportedOperationException
        // list.remove("hello"); // java.lang.UnsupportedOperationException
        System.out.println(list.get(0));
        list.set(1, "javase");
        System.out.println(list);
    }

    /**
     * public static <E> List<E> of(E... elements):返回包含任意数量元素的不可变列表
     */
    public static void test02() {
        List<String> list = List.of("hello", "world", "java", "java");
        list.add("javaee"); // UnsupportedOperationException
        list.remove("java"); // UnsupportedOperationException
        list.set(1, "javaee"); // UnsupportedOperationException
        System.out.println(list);
    }

    /**
     * public static <E> Set<E> of(E... elements) :返回一个包含任意数量元素的不可变集合
     */
    public static void test03() {
        // Set<String> set = Set.of("hello", "world", "java", "world"); // IllegalArgumentException
        Set<String> set = Set.of("hello", "world", "java");
        set.add("javaee"); // UnsupportedOperationException
        set.remove("world"); // UnsupportedOperationException
        System.out.println(set);
    }
}