Java 高级_泛型(Generic)

163 阅读9分钟

泛型(标签)

把元素的类型设计成一个参数,这个类型参数叫做泛型

为什么要有泛型

  • 解决元素存储的安全性问题
  • 解决获取数据元素时,需要类型强制转换的问题

泛型的概念

  • 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者时某个方法的返回值及参数类型;这个类型参数将在使用时确定
  • jdk1.5后,java引入了"参数化类型"的概念,允许我们在创建集合时再指定集合元素的类型,如 List<String>,这表明该 List只能存储字符串类型的对象

在集合中使用泛型

package com.atguigu.java;

import org.junit.Test;

import java.util.*;

/**
 *
 * 泛型的使用
 *
 * 在集合中使用泛型:
 * 总结:
 * 1.集合接口或集合类在 jdk1.5时都修改为带泛型的结构
 * 2.在实例化集合类时,可以指明具体的泛型类型
 * 3.指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(方法、构造器、变量...)使用到类的泛型的位置,
 * 都指定为实例化的泛型类型,比如:add(E e) -> add(String, e)
 *
 * 4.注意点:泛型的类型必须是类,不能是基本数据类型;
 * 需要用到基本数据类型的位置需要,拿包装类替换
 *
 * 5.如果实例化时,没有指明泛型的类型;默认类型为 java.lang.Object类型
 *
 *
 * @author lv
 * @create 2021-01-11 20:07
 */
public class GenericTest {
    @Test
    public void test2 () {
        HashMap<String, Integer> map = new HashMap<String, Integer>();
        map.put("age", 45);
        map.put("gender", 1);
        map.put("Jack", 1);
        map.put("Tom", 1);

//        泛型的嵌套
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            String key = entry.getKey();
            int value = entry.getValue();
            System.out.println("entry: " + key + " :" + value);
        }
    }
//    在使用泛型以后
    @Test
    public void test1 () {
//        泛型不能是基本数据类型
        ArrayList<Integer> list = new ArrayList<Integer>();

        list.add(78);
        list.add(87);
        list.add(88);
        list.add(99);
        list.add(96);

//        编译时,就会进行类型检查,保证数据安全
//        list.add("Tom"); error

        for (Integer score : list) {
//            避免了强转操作
            int stuScore = score;
            System.out.println("student: " + stuScore);
        }

        Iterator<Integer> i1 = list.iterator();
        while (i1.hasNext()) {
            int score = i1.next();
            System.out.println("stu: " + score);
        }
    }
//    在使用泛型以前
    @Test
    public void test () {
        ArrayList list = new ArrayList();
        list.add(78);
        list.add(76);
        list.add(89);
        list.add(88);

//        问题一:类型不安全
//        list.add("Tom");

        for (Object score : list) {
//            System.out.println(score);

//            问题二:强转时可能出现 ClassCastException
            int stuScore = (Integer)score; // 自动拆箱
//            int stuScore = (int)score; // 自动转化
            System.out.println(stuScore);
        }

    }
}

自定义泛型结构

泛型类、泛型接口

package com.atguigu.java;

import org.junit.Test;

/**
 *
 * 泛型结构:泛型类、泛型接口、泛型方法
 *
 * 1.泛型类、泛型接口
 *
 *
 * @author lv
 * @create 2021-01-12 20:19
 */
public class GenericTest1 {
    @Test
    public void test1 () {
        SubOrder s1 = new SubOrder();
//        由于子类在继承泛型的父类时,指明了泛型类型,则实例化子类对象时,不再需要指明泛型类型
        s1.setOrderT(123);

//        泛型子类
        SubOrder1<String> s11 = new SubOrder1<>();
        s11.setOrderT("ACB");
    }
    @Test
    public void test () {
//        如果定义了泛型类,实例化时没有指明类的泛型,则认为此泛型类型为 Object类型
//        要求:如果大家定义了类是带有泛型的,建议在实例化时要指明类的泛型
        Order order = new Order();
        order.setOrderT(123);
        order.setOrderT("abc");
        order.setOrderT(789);

//        实例化时指明类的泛型
        Order<String> order1 = new Order<>("orderAa", 1001, "qaz");
        order1.setOrderT("AAA");
    }
}

package com.atguigu.java;

/**
 * @author lv
 * @create 2021-01-12 20:08
 */
public class Order<T> {
//    泛型的表示符 T E K V ...
    String orderName;
    int orderId;

//    类型的内部结构可以使用类的泛型
    T orderT;

    public Order () {
    
//        编译不通过
//        T[] arr = new T[];
//        进行强转,编译通过
        T[] arr = (T[]) new Object[10];

    }
    public Order (String orderName, int orderId, T orderT) {
        this.orderName = orderName;
        this.orderId = orderId;
        this.orderT = orderT;
    }
    public T getOrderT () {
        return orderT;
    }
    public void setOrderT (T orderT) {
        this.orderT = orderT;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderName='" + orderName + '\'' +
                ", orderId=" + orderId +
                ", orderT=" + orderT +
                '}';
    }
}

package com.atguigu.java;

/**
 * @author lv
 * @create 2021-01-12 20:31
 */
public class SubOrder extends Order<Integer> {
//    SubOrder 不是泛型类
}

package com.atguigu.java;

/**
 * @author lv
 * @create 2021-01-12 20:38
 */
public class SubOrder1<T> extends Order<T> {
//    SubOrder1 是泛型类
}

泛型方法

在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系;换句话说,泛型方法所属的类是不是泛型类都没有关系

示例:public [static] <E> TypeClass<E> xxXxx (E[] e) {}

  • 泛型方法,可以声明为静态的;泛型参数是在调用方法时确定的,并非在实例化类时确定的
// public static <E> type<E> xxXxx (E[] e) {}

package com.atguigu.java;

import java.util.ArrayList;
import java.util.List;

/**
 * 1.泛型类、接口
 * 2.泛型方法
 *
 * @author lv
 * @create 2021-01-12 20:08
 */
// 1.泛型类、接口
public class Order<T> {
//    泛型的表示符 T E K V ...
    String orderName;
    int orderId;

//    类型的内部结构可以使用类的泛型
    T orderT;

    public Order () {
//        编译不通过
//        T[] arr = new T[];
//        进行强转,编译通过
        T[] arr = (T[]) new Object[10];

    }
    public Order (String orderName, int orderId, T orderT) {
        this.orderName = orderName;
        this.orderId = orderId;
        this.orderT = orderT;
    }
    public T getOrderT () {
        return orderT;
    }
    public void setOrderT (T orderT) {
        this.orderT = orderT;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderName='" + orderName + '\'' +
                ", orderId=" + orderId +
                ", orderT=" + orderT +
                '}';
    }

//    2.泛型方法
    public static <E> List<E> copyFromArrayToList (E[] arr) {
        ArrayList<E> list = new ArrayList<>();
        for (E e : arr) {
            list.add(e);
        }
        return list;
    }
}

// *********************************************
    @Test
    public void test3 () {
//        泛型方法
        Order order = new Order();
        Integer[] integers = {1, 2, 3, 4};
//        泛型方法在调用时,指明泛型参数的类型
        List<Integer> list1 = order.copyFromArrayToList(integers);
        System.out.println(list1); // [1, 2, 3, 4]

    }

注意:

  • 泛型不同的引用不能相互赋值
    @Test
    public void test2 () {
        ArrayList<String> list = null;
        ArrayList<Integer> list1 = null;
//        list1 = list; error
    }
  • 泛型要么一路都用,要么一路都不用

  • 如果泛型是一个接口或抽象类,则不可创建泛型类的对象

  • 在类、接口上声明的泛型,在静态方法中不能使用类的泛型

  • 异常类不能有泛型结构

  • 实际使用中的问题

    public Order () {
//        编译不通过
//        T[] arr = new T[];
//        进行强转,编译通过
        T[] arr = (T[]) new Object[10];
    }
  • 子类父类的泛型
class Father<T1, T2> {}
// 不保留
class Son1 extends Father {
// 等价于 Father<Object, Object>
}
// 具体类型
class Son2 extends Father<Integer, String> {
}
// 全部保留
class Son3<T1, T2> extends Father<T1, T2> {
}
// 部分保留
class Son4<T2> extends Father<Integer, T2> {
}

泛型在继承上的体现

  • 类A是类B的父类,但是 G<A>和 G<B> 不具备子父类关系
  • 类A是类B的父类,则 A<G> 是 B<G> 的父类
package com.atguigu.java1;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

/**
 * @author lv
 * @create 2021-01-13 19:42
 *
 * 1.泛型在继承方面的体现
 *
 * 2.通配符的使用
 */
public class GenericTest {
    @Test
    public void test1 () {
        List<String> strList = null;
        ArrayList<String> list = new ArrayList<>();
        strList = list;
    }
    /**
     * 1.泛型在继承方面的体现
     * 类A是类B的父类,但是 G<A>和 G<B> 不具备子父类关系
     */
    @Test
    public void test () {

        Object obj = null;
        String str = null;
        obj = str;

        Object[] arrObj = null;
        String[] arrStr = null;
        arrObj = arrStr;

        List<Object> list1 = null;
        List<String> list2 = null;
//        此时的 list1和list2 的类型不具有子父类关系
//        list1 = list2; error

    }
}

通配符的使用

  • 添加:对于 List<?>就不能向其内部添加数据,null除外
  • 获取:可以获取数据,数据类型为 Object
    /**
     * 2.泛型通配符的使用
     * 通配符:'?'
     * 类A是类B的父类,G<A>和G<B>是没有关系的,
     * 二者共同的父类是 G<?>
     */
    @Test
    public void test2 () {
        List<Object> objList = null;
        List<String> strList = null;
        strList = new ArrayList<>();
        strList.add("451");
        strList.add("sdfv");
        strList.add("5df15");
        strList.add("sdf45");

        List<?> list = null;
        list = objList;
        list = strList;
//        添加:对于 List<?>就不能向其内部添加数据
//        除了添加 null之外
//        list.add("456"); error
        list.add(null);
        System.out.println(list); // [451, sdfv, 5df15, sdf45, null]

//        获取:可以获取数据,数据类型为 Object
        Object o = list.get(0);
        System.out.println(o); // 451
        print(list);
    }
    public void print (List<?> list) {
        Iterator<?> iterator = list.iterator();
        while (iterator.hasNext()) {
            Object next = iterator.next();
            System.out.println(next);
        }
    }

有限制条件的通配符

通配符指定上限

  • 上限 extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即 <=
  • G<? extneds A> 可以作为 G<A>和G<B>的父类,其中 B是 A的子类
// 只允许泛型为 Number及Number子类的引用调用
<? extends Number> (无穷小,Number)]

通配符指定下限

  • 下限 super:使用时指定的类型不能小于操作的类,即 >=
  • G<? super A> 可以作为 G<A>和G<B>的父类,其中 B是 A的父类
// 只允许泛型为 Number及 Number父类的引用调用
<? super Number> [Number, 无穷大)
package com.atguigu.java2;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

/**
 * @author lv
 * @create 2021-01-14 21:02
 */
public class GenericTest {
    @Test
    public void test () {
        List<? extends Person> list1 = null;
        List<? super Person> list2 = null;

        List<Student> list3 = new ArrayList<>();
        List<Person> list4 = new ArrayList<>();
        List<Object> list5 = new ArrayList<>();

        list1 = list3;
        list1 = list4;
//        <= Person
//        list1 = list5; error

//        >= Person
//        list2 = list3; error
        list2 = list4;
        list2 = list5;

//        读取数据:
        list1 = list4;
        Person p1 = list1.get(0);
        Object o1 = list2.get(0);

//        写入数据:
//        list1.add(new Student()); error
//        list1.add(new Person()); error

        list2.add(new Person());
        list2.add(new Student());
    }
}

泛型应用举例

package com.atguigu.java1;

import java.util.List;

/**
 * @author lv
 * @create 2021-01-13 19:09
 *
 * DAO:data(base) access object(数据访问对象)
 *
 * 1.定义数据库的通用操作
 *
 */
public class DAO<T> {
//    表的共性操作

//    添加一条记录
    public void add (T t) {
        //
    }

//    删除一条记录
    public boolean remove (int index) {
        return true;
    }

//    修改一条记录
    public boolean fix (int index, T t) {
        return false;
    }

//    查询一条记录
    public T get (int index) {
        return null;
    }

//    查询多条记录
    public List<T> getForList (int index) {
        return null;
    }

//    泛型方法
    public <E> E getValue (E e) {
        return null;
    }

}
package com.atguigu.java1;

/**
 * @author lv
 * @create 2021-01-13 19:21
 */
public class CustomerDAO extends DAO<Customer> {
}
package com.atguigu.java1;

/**
 * @author lv
 * @create 2021-01-13 19:22
 */
public class Customer {
}
package com.atguigu.java1;

import org.junit.Test;

import java.util.List;

/**
 * @author lv
 * @create 2021-01-13 19:26
 */
public class DAOTest {
    @Test
    public void test () {
        CustomerDAO d1 = new CustomerDAO();
        d1.add(new Customer());
        List<Customer> list1 = d1.getForList(5);

        StudentDAO s1 = new StudentDAO();
        s1.add(new Student());
        List<Student> list2 = s1.getForList(1);
    }
}

// **************************************************
package com.atguigu.exer1;

import java.util.*;

/**
 * @author lv
 * @create 2021-01-18 19:14
 */
public class DAO<T> {

    private Map<String, T> map = new HashMap<>();

    public void save (String id, T entity) {
        map.put(id, entity);
    }
    public T get (String id) {
        T t = map.get(id);
        return t;
    }
    public void update (String id, T entity) {
        if (map.containsKey(id)) {
            map.put(id, entity);
        }
    }
    public List<T> list () {
//        error:
//        本身就是 Collection 所以不能强转为 List
//        Collection<T> values = map.values();
//        List<T> list = (List<T>) values;
//        return list; error

//        Set<String> strings = map.keySet();
//        List<T> list = new ArrayList<>();
//        for (String o : strings) {
//            list.add(map.get(o));
//        }

        List<T> list = new ArrayList<>();
        Collection<T> values = map.values();
        for (T v : values) {
            list.add(v);
        }

        return list;
    }
    public void delete(String id) {
        if (map.containsKey(id)) {
            map.remove(id);
        }
    }

}
package com.atguigu.exer1;

/**
 * @author lv
 * @create 2021-01-18 20:01
 */
public class User {

    private int id;
    private int age;
    private String name;

    public User() {
    }

    public User(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (id != user.id) return false;
        if (age != user.age) return false;
        return name != null ? name.equals(user.name) : user.name == null;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + age;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
package com.atguigu.exer1;

import java.util.List;


/**
 * @author lv
 * @create 2021-01-18 20:06
 */
public class DAOTest {

    public static void main(String[] args) {
        DAO<User> userDAO = new DAO<>();
        userDAO.save("1001", new User(1001, 25, "lv"));
        userDAO.save("1002", new User(1002, 55, "ldfv"));
        userDAO.save("1003", new User(1003, 25, "ldfbv"));
        userDAO.save("1004", new User(1004, 25, "lvdbf"));

        userDAO.update("1003", new User(1003, 88, "zhouhzou"));

        List<User> list = userDAO.list();
        System.out.println(list);
//        [User{id=1004, age=25, name='lvdbf'}, User{id=1003, age=25, name='ldfbv'}, User{id=1002, age=55, name='ldfv'}, User{id=1001, age=25, name='lv'}]
        list.forEach(System.out::println);
        /**
         * User{id=1004, age=25, name='lvdbf'}
         * User{id=1003, age=25, name='ldfbv'}
         * User{id=1002, age=55, name='ldfv'}
         * User{id=1001, age=25, name='lv'}
         */
    }
}

遍历 Map的 key集、value集、key-value集,并使用泛型

    @Test
    public void test5 () {
        Map<String, Integer> map = new HashMap<>();
        map.put("name", 456);
        map.put("age", 789);
        map.put("address", 123);

        Set<String> keys = map.keySet();
        for (String s : keys) {
            System.out.println(s);
        }

        Collection<Integer> values = map.values();
        for (Integer i : values) {
            System.out.println(i);
        }
        Iterator<Integer> values1 = values.iterator();
        while (values1.hasNext()) {
            System.out.println(values1.next());
        }
        Set<Map.Entry<String, Integer>> entry = map.entrySet();
        Iterator<Map.Entry<String, Integer>> entrys = entry.iterator();
        while (entrys.hasNext()) {
            Map.Entry<String, Integer> e = entrys.next();
            String key = e.getKey();
            Integer value = e.getValue();
            System.out.println("key: " + key + " value: " + value);
        }
    }

提供一个方法,用于遍历获取 Map<String, String>中的所有value,并放在 List中返回

    @Test
    public void test6 () {
        HashMap<String, String> map = new HashMap<>();
        List<String> list = null;
        map.put("name", "Tom");
        map.put("age", "Tom1");
        map.put("addr", "Tom2");
//        Set<String> strings = map.keySet();
//        for (String s : strings) {
//            list.add(map.get(s));
//        }
        list = getList(map);
        System.out.println(list);

    }
    public <T> List<T> getList (Map<String, T> map) {
        List<T> list = new ArrayList<>();
//        Set<String> strings = map.keySet();
//        for (String s : strings) {
//            list.add(map.get(s));
//        }
        Collection<T> values = map.values();
        for (T v : values) {
            list.add(v);
        }
        return list;
    }
//    public List<String> getList (Map<String, String> map) {
//        List<String> list = new ArrayList<>();
//        Set<String> strings = map.keySet();
//        for (String s : strings) {
//            list.add(map.get(s));
//        }
//        return list;
//    }

创建一个与 a.txt文件同目录下的另一个文件 b.txt

    @Test
    public void test7 () throws IOException {
        File file = new File("hi.txt");
        String parentPath = file.getParent();
        File file1 = new File(parentPath, "hello.txt");
        boolean newFile = file1.createNewFile();
        if (newFile) {
            System.out.println("file create Success!");
        } else {
            boolean delete = file1.delete();
            if (delete) {
                System.out.println("file Delete!");
            }
        }
    }

Map接口的常用方法

  • 增:put(K k, V v)
  • 删:V remove(K k)
  • 改:put(K k, V v)
  • 查:V get(K k)
  • 长度:int size()
  • 遍历:keySet()、values()、entrySet()