Java泛型,超详细整理,适合新手入门

标题

  • 泛型语法
    • 1.1 泛型的引出
    • 1.2 泛型介绍
    • 1.3 泛型语法
    • 1.4 泛型使用细节
    • 1.5 课堂练习
  • 2.自定义泛型
    • 2.1自定义泛型-类
    • 2.2自定义泛型-接口
    • 2.3自定义泛型-方法
    • 2.4练习题
  • 3.范型继承和通配符
    • 3.1JUnit单元测试框架
    • 3.2练习题

Java泛型,超详细整理,适合新手入门_第1张图片

泛型语法

1.1 泛型的引出

  1. 传统方法不能对加入到集合中的数据类型进行约束;
  2. 对集合遍历的时候需要进行类型转换,效率低;
public class Generic01 {
	public static void main(String[] args)ArrayList arrayList = new ArrayList();
		arrayList.add(new Dog("scott",4));
		arrayList.add(new Dog("jack",5));
		arrayList.add(new Dog("mary",6));
		for(Object o : arrayList) {
			Dog dog = (Dog) o;//获取对象的名字
			System.out.println(dog.getName());
		}}
class Dog {
	private String name;
	private int age;
	public Dog(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getAge() {
		return age;
	}
}
  1. 引出:new ArrayList(),表示存放到ArrayList 集合中的元素是Dog类型
  2. 优点
    • 如果编译器发现添加的类型不满足要求,就会报错,即编译时编译器会检查添加元素的类型,提高安全性;
    • 遍历时,可以直接取出Dog类型,而不是Object类型,减少了类型转换的次数;
public class Generic01 {
	public static void main(String[] args)ArrayList arrayList = new ArrayList<Dog>();
		arrayList.add(new Dog("scott",4));
		arrayList.add(new Dog("jack",5));
		arrayList.add(new Dog("mary",6));
		//arrayList.add(new Cat("招财猫",3));
		for(Dog dog : arrayList) {
			System.out.println(dog.getName());
		}
	}
}

不使用泛型:放入到ArrayList会先转成Object,在取出时,再转换成Dog;
使用泛型:放入和取出时,不需要类型转换;

不使用泛型 使用泛型
放入到ArrayList会先转成Object,在取出时,再转换成Dog; 放入和取出时,不需要类型转换;

1.2 泛型介绍

  1. 泛型是可以表示数据类型的一种数据类型;
  2. 泛型又称为参数化类型,是jdk5.0出现的新特性,可以解决数据类型的安全性问题;
  3. 在类声明或者实例化的时候只需要指定需要的具体类型即可;
  4. Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException的异常;同时,代码更简洁、健壮。
  5. public class ArrayList ,相当于把Dog赋给了E,Dog->E
  6. 泛型的作用是: 可以在类声明时通过一个标识符表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
    - 参数的类型
    - 类中方法返回值的类型
    - 属性的类型
泛型 Generic
参数的类型 $1600
类中方法返回值的类型 $12
属性的类型 $1
  1. 在这里,E的类型一定要清楚的知道,可以通过getClass()方法得到;
  2. 注意:E具体的数据类型在定义Person对象时指定,即在编译期间就已确定类型;
    Java泛型,超详细整理,适合新手入门_第2张图片

1.3 泛型语法

  1. interface 接口 {} 和 class 类{}
  • 说明:
    • T、K、V不代表值,而是表示类型;
    • 任意字母都可以,常用T表示,T是Type的缩写
  1. 泛型的实例化:要在类名后面指定类型参数的值(类型),如:
    • (1)List stringList = new ArrayList();
    • (2)Interator iterator = dog.iterator();
      Java泛型,超详细整理,适合新手入门_第3张图片
      Java泛型,超详细整理,适合新手入门_第4张图片

1.4 泛型使用细节

  1. interface List {}, public class HashSet {};
    T,E只能是引用类型,不能用基本数据类型;(Type argument cannot be of primitive type)
    Java泛型,超详细整理,适合新手入门_第5张图片
  2. 在给泛型指定具体类型后,可以传入该类型或其子类类型;
    Java泛型,超详细整理,适合新手入门_第6张图片
  3. 泛型的简写形式:省略构造器处的泛型表示,编译器会进行类型推断;
    List list = new ArrayList<>();
  4. 如果没有指定泛型,默认泛型是Object;
    Map hashMap = new HashMap();
    等价于
    Map hashMap = new HashMap<>();
    Java泛型,超详细整理,适合新手入门_第7张图片
    Java泛型,超详细整理,适合新手入门_第8张图片

1.5 课堂练习

定义Employee

  1. 该类包括: private成员变量name, sal, birthday, 其中birthdayMyDate 类的对象;

  2. 为每一个属性定义 getter, setter 方法;

  3. 重写 toString 方法输出 name, sal, birthday

  4. MyDate类包含: private成员变量month, day, year; 并为每一个属性定义 getter, setter 方法;

  5. 创建该类的 3 个对象, 并把这些对象放入到 ArrayList 集合中 (ArrayList 需使用泛型来定义), 对集合中的元素进行排序, 并遍历输出.

排序方式: 调用 ArrayListsort 方法, 传入 Comparator 对象 [使用泛型], 先按照 name 排序, 如果 name 相同, 则按照生日日期的先后排序.[即: 定制排序]


代码
MyDate类

/*
MyDate类包含: private成员变量month, day, year;
并为每一个属性定义 getter, setter 方法;
*/
public class MyDate {
    private Integer year;
    private Integer month;
    private Integer day;

    public MyDate(Integer year, Integer month, Integer day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    //setter, getter方法, toString方法
}

Employee类

/*
定义Employee类
1) 该类包括: private成员变量name, sal, birthday, 其中birthday 为 MyDate 类的对象;
2) 为每一个属性定义 getter, setter 方法;
3) 重写 toString 方法输出 name, sal, birthday
*/
public class Employee {
    private String name;
    private double sal;
    private MyDate birthday;

    public Employee(String name, double sal, MyDate birthday) {
        this.name = name;
        this.sal = sal;
        this.birthday = birthday;
    }
    //setter, getter方法, toString方法
}

GenericExercise02

/*
创建该类的 3 个对象, 并把这些对象放入到 ArrayList 集合中
(ArrayList 需使用泛型来定义), 对集合中的元素进行排序, 并遍历输出.

排序方式: 调用 ArrayList 的 sort 方法, 传入 Comparator 对象 [使用泛型],
先按照 name 排序, 如果 name 相同, 则按照生日日期的先后排序.[即:定制排序]
*/
public class GenericExercise02 {
    public static void main(String[] args) {

        List<Employee> arrayList = new ArrayList<>();
        arrayList.add(new Employee("tom", 45000, new MyDate(1999, 12, 21)));
        arrayList.add(new Employee("tom", 35000, new MyDate(1999, 12, 25)));
        arrayList.add(new Employee("mary", 40000, new MyDate(1998, 5, 12)));

        System.out.println(arrayList);

        //对员工雇员进行排序
        arrayList.sort(new Comparator<Employee>() {
            @Override
            public int compare(Employee emp1, Employee emp2) {
                //先按照 name 排序, 如果 name 相同, 则按照生日日期的先后排序.[即:定制排序]
                //先对传入的参数进行验证
                if (!(emp1 instanceof Employee && emp2 instanceof Employee)) {
                    return 0;
                }
                //比较名字
                int i = emp1.getName().compareTo(emp2.getName());
                if (i != 0) {
                    return i;
                }
                //下面是对MyDate类型的birthday对象的比较. 因此, 最好把这个比较, 放在MyDate类完成
                //如果名字相同, 就比较birthday
                int yearMinus = emp1.getBirthday().getYear() - emp2.getBirthday().getYear();
                if (yearMinus != 0) {
                    return yearMinus;
                }
                //如果year相同, 就比较month
                int monthMinus = emp1.getBirthday().getMonth() - emp2.getBirthday().getMonth();
                if (monthMinus != 0) {
                    return monthMinus;
                }
                //如果month相同, 就比较day
                int dayMinus = emp1.getBirthday().getDay() - emp2.getBirthday().getDay();
                if (dayMinus != 0) {
                    return dayMinus;
                }
                return 0;
            }

        });

        System.out.println("===排序后的结果===");
        System.out.println(arrayList);
    }
}

改进后:封装后代码复用性更高
MyDate 实现Comparable接口, 指定泛型, 实现compareTo方法

public class MyDate implements Comparable<MyDate> {
    private Integer year;
    private Integer month;
    private Integer day;
    
    @Override
    public int compareTo(MyDate o) {把对year-month-day的比较放在这里
        int yearMinus = this.year - o.getYear();
        if (yearMinus != 0) {
            return yearMinus;
        }
        //如果year相同, 就比较month
        int monthMinus = this.month - o.getMonth();
        if (monthMinus != 0) {
            return monthMinus;
        }
        //如果month相同, 就比较day
        int dayMinus = this.day - o.getDay();
        if (dayMinus != 0) {
            return dayMinus;
        }
        return 0;
    }
}

GenericExercise02改进

//下面是对MyDate类型的birthday对象的比较. 因此, 最好把这个比较, 放在MyDate类完成
//如果名字相同, 就比较birthday
//封装后, 将来可维护性和复用性, 就大大增强
/*int yearMinus = emp1.getBirthday().getYear() - emp2.getBirthday().getYear();
if (yearMinus != 0) {
    return yearMinus;
}
//如果year相同, 就比较month
int monthMinus = emp1.getBirthday().getMonth() - emp2.getBirthday().getMonth();
if (monthMinus != 0) {
    return monthMinus;
}
//如果month相同, 就比较day
int dayMinus = emp1.getBirthday().getDay() - emp2.getBirthday().getDay();
if (dayMinus != 0) {
    return dayMinus;
}
return 0;*/
return emp1.getBirthday().compareTo(emp2.getBirthday());

Comparator实现定制排序传送门

2.自定义泛型

2.1自定义泛型-类

  1. 基本语法:class 类名 {} 可以有多个泛型
  2. 普通成员可以使用泛型(属性、方法);
  3. 使用泛型的数组不能初始化;
  4. 静态成员中不能使用类的泛型;
  5. 泛型类的类型,是在创建对象时确定的;(因为在创建对象时,需要指定类型)
  6. 如果在创建对象时,没有指定类型,默认为Object;
public class CustomGeneric {
    public static void main(String[] args) {
        //T是Double,R是String,M是Integer
        Tiger<Double, String, Integer> tiger = new Tiger<>("john~~");
        tiger.setT(3.2);//OK
        System.out.println(tiger);
        Tiger tiger1 = new Tiger("john");
        //tiger.setT("yy");因为T是Object,"yy"是 Object子类,类型不对, 貌似字符串可以赋给Object类型
        
    }
}
//1.Tiger后面有泛型,所以我们把Tiger 称为自定义泛型类
//2.T, R, M 泛型的标识符,一般是单个大写字母
//3.泛型的标识符可以有多个
//4.普通成员可以使用泛型 (属性、方法)
//5.使用泛型的数组,不能初始化
class Tiger<T, R, M> {
    String name;
    T t;//属性使用到泛型
    M m;
    R r;

    public Tiger(String name) {
        this.name = name;
    }
    //Type parameter 'T' cannot be instantiated directly
    //T[] ts = new T[8];类型不能确定,无法分配空间,因为数组在new的时候不能确定T的类型,就无法在内存开辟空间
    public Tiger(T t, M m, R r) {//构造器使用泛型
        this.t = t;
        this.m = m;
        this.r = r;
    }
    //因为静态是和类相关的,在类加载时,对象还没有创建
    //所以,如果静态方法和静态属性使用了泛型,JVM就无法完成初始化
    /*static R r2;
    public static void m1(M m) {//cannot be referenced from a static context

    }*/

    //方法使用泛型

    public String getName() {
        return name;
    }

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

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }

    public M getM() {
        return m;
    }

    public void setM(M m) {
        this.m = m;
    }

    public R getR() {//返回类型可以使用泛型
        return r;
    }

    public void setR(R r) {//方法使用到泛型
        this.r = r;
    }
}

2.2自定义泛型-接口

基本语法:interface 接口名 {}

  1. 接口中静态成员也不能使用泛型;(和泛型类规定一样)
  2. 泛型接口的类型,在继承接口或者实现接口时确定;
  3. 如果没有指定类型,默认为Object;(不规范,建议指定
  4. 方法参数列表中的泛型直接给值,类定义时的泛型给类型;
  5. /**
     * 泛型接口使用说明
     * 1.接口中,静态成员不能使用泛型
     * 2.泛型接口的类型是在继承接口或者实现接口时实现的
     */
    
    //1.在继承接口时,指定泛型接口的类型
    interface IA extends IUsb<String, Double> {
    
    }
    //当我们去实现IA接口时,因为IA在继承IUsb接口时,指定了U为String,R为Double
    // 因此在实现IUsb接口方法时,使用String替换U,Double替换R
    class AA implements IA {
    
        @Override
        public Double get(String s) {
            return null;
        }
    
        @Override
        public void hi(Double aDouble) {
    
        }
    
        @Override
        public void run(Double r1, Double r2, String u1, String u2) {
    
        }
    }
    //2.实现接口时,直接指定泛型接口的类型
    //给U指定了Integer, 给R指定了Float
    //所以当实现IUsb接口方法时,会使用Integer替换U,Float替换R
    class BB implements IUsb<Integer, Float> {
    
        @Override
        public Float get(Integer integer) {
            return null;
        }
    
        @Override
        public void hi(Float aFloat) {
    
        }
    
        @Override
        public void run(Float r1, Float r2, Integer u1, Integer u2) {
    
        }
    }
    
    //3.没有指定泛型,就默认为Object
    //等价于class CC implements IUsb {}, 但是不规范
    class CC implements IUsb {
        @Override
        public Object get(Object o) {
            return null;
        }
    
        @Override
        public void hi(Object o) {
    
        }
    
        @Override
        public void run(Object r1, Object r2, Object u1, Object u2) {
    
        }
    
    }
    interface IUsb<U, R> {//U=Integer, R=Float
        //接口中普通方法,可以使用泛型
        R get(U u);
    
        int n = 10;
        //U name = "3e";静态成员不能使用泛型
        void hi(R r);
    
        void run(R r1, R r2, U u1, U u2);
    
        //在jdk8以后,可以在接口中使用默认方法,在默认方法中可以使用泛型
        default R method(U u) {
            return null;
        }
    }
    

    2.3自定义泛型-方法

    基本语法:修饰符[空格] 返回类型 方法名(参数列表) {}

    1. 泛型方法,可以定义在普通类中,也可以定义在泛型类中;
    2. 当泛型方法被调用时,泛型类型就会确定,否则调用不起来;
    3. public void ear(E e) {},修饰符后面没有 , 那么eat方法就不是泛型方法,只是使用了泛型;
    4. 泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型;
    public class CustomMethodGeneric_ {
        public static void main(String[] args) {
            Car car = new Car();
            //2. 当泛型方法被调用时,泛型类型就会确定,否则调用不起来;
            car.fly("宝马",1200);//当调用方法时,传入参数,编译器就会确定类型
            System.out.println("===========");
            car.fly(2.2,1200);//当调用方法时,传入参数,编译器就会确定类型
            System.out.println("===========");
            //T:Sring, R:ArrayList
            Fish<String, ArrayList> fish = new Fish<>();
            //K:Float
            fish.hello(new ArrayList(), 2.1f);
        }
    }
    //1.泛型方法,可以定义在普通类中
    class Car {//普通类
        public void run() {//普通方法
    
        }
        //说明:泛型方法
        //  就是泛型
        // 提供给fly方法使用的
        public <T, R> void fly(T t, R r) {//泛型方法
            System.out.println(t.getClass());//String
            System.out.println(r.getClass());//Integer
        }
    }
    //2.泛型方法,也可以定义在泛型类中
    class Fish<T, R> {//泛型类
        public void run() {//普通方法
    
        }
    
        public <U, M> void fly(U u, M m) {//泛型方法
    
        }
        //说明
        //  hi()方法不是一个泛型方法
        //  只是hi()方法使用了类声明的泛型
        public void hi(T e) {
    
        }
        //泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型
        public <K> void hello(R r, K k) {
            System.out.println(r.getClass());//ArrayList
            System.out.println(k.getClass());//Float
        }
    }
    

    2.4练习题

    public class CustomMethodGenericExercise {
        public static void main(String[] args) {
            //T:String, R:Integer, M:Double
            Apple<String, Integer, Double> apple = new Apple<>();
            apple.fly(10);//泛型都是引用类型,所以10自动装箱,输出:Integer
            apple.fly(new Dog());//输出:Dog
        }
    }
    
    class Apple<T, R, M> {
        public <E> void fly(E e) {
            //.getClass():显示包名+类名; 
            //.getClass().getSimpleName():只显示类名
            System.out.println(e.getClass().getSimpleName());
        }
        //public void eat(U u) {} //错误,U没有声明
        public <U> void eat(U u) {} //改正
        public void run(M m) {}
    
    }
    class Dog {}
    

    3.范型继承和通配符

    1. 泛型不具备继承性;
      List list = new ArrayList ();//这样写是错误的
    2. 通配符起到约束作用;
    3. 通配符 作用
      List c 表示可以接收任意范型类型
      List<? extends A> c 表示可以接收A类或者A类的子类,规定泛型上限
      List c 表示可以接收A类或者A类的父类(不限于直接父类),规定泛型下限
      @SuppressWarnings({"all"})
      public class GenericExtends {
          public static void main(String[] args) {
              //范型没有继承性
              //List list = new ArrayList();
              
              //举例说明下面三个方法的使用;
              List<Object> list1 = new ArrayList<>();
              List<String> list2 = new ArrayList<>();
              List<AA> list3 = new ArrayList<>();
              List<BB> list4 = new ArrayList<>();
              List<CC> list5 = new ArrayList<>();
              
              //1.如果是 List c, 可以接收任意的范型类型
              printCollection1(list1);
              printCollection1(list2);
              printCollection1(list3);
              printCollection1(list4);
              printCollection1(list5);
              
              //2.如果是 List c, 可以接收AA或者AA的子类
              //printCollection2(list1);
              //printCollection2(list2);
              printCollection2(list3);
              printCollection2(list4);
              printCollection2(list5);
      
              //3.如果是 List c, 可以接收AA,AA的父类但不限于直接父类
              printCollection3(list1);//正确
              //printCollection3(list2);
              printCollection3(list3);
              //printCollection3(list4);
              //printCollection3(list5);
          }
      
          public static void printCollection1(List<?> c) {
              for (Object object : c) {//通配符,取出时就是Object
                  System.out.println(object);
              }
          }
      
          public static void printCollection2(List<? extends AA> c) {
              for (AA object : c) {
                  System.out.println(object);
              }
          }
      
          //List 表示任意范型类型都可以接收
          //List 表示的是上限,可以接收 AA或AA的子类
          //List 表示的是下限,可以接收 AA或者AA的父类,不限于直接父类
          public static void printCollection3(List<? super AA> c) {
              for (Object object : c) {
                  System.out.println(object);
              }
          }
      }
      class AA {
      
      }
      class BB extends AA {
      
      }
      class CC extends BB {
      
      }
       
        

      3.1JUnit单元测试框架

      shortcuts: Alt+Enter/option+Enter
      可以直接运行一个方法,JUnit测试框架;
      Java泛型,超详细整理,适合新手入门_第9张图片

      public class JUnit_ {
          public static void main(String[] args) {
              //new JUnit_().m1();
              //new JUnit_().m2();
          }
          @Test
          public void m1() {
              System.out.println("m1()方法");
          }
          @Test
          public void m2() {
              System.out.println("m2()方法");
          }
      }
      

      3.2练习题

      public class Homework01 {
          public static void main(String[] args) {
      
          }
      
          @Test
          public void test() {
              //这里我们给泛型T指定的类型是User
              DAO<User> userDAO = new DAO<>();
              userDAO.save("No1", new User(1, 22));
              userDAO.save("No2", new User(2, 23));
              userDAO.save("No3", new User(3, 23));
              List<User> list = userDAO.list();
              userDAO.update("No3", new User(3, 18));
              list = userDAO.list();
              userDAO.delete("No3");
              list = userDAO.list();
              User user = userDAO.get("No2");
              System.out.println(list);
              System.out.println(user.getAge());
          }
      }
      
      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) {
              return map.get(id);
          }
      
          public void update(String id, T entity) {
              map.put(id, entity);
          }
      
          public List<T> list() {
              List<T> arrayList = new ArrayList<>();
              Set<String> keySet = map.keySet();
              Iterator<String> iterator = keySet.iterator();
              while (iterator.hasNext()) {
                  String key = iterator.next();
                  arrayList.add(get(key));
              }
              return arrayList;
          }
      
          public void delete(String id) {
              map.remove(id);
          }
      }
      class User {
          private int id;
          private int age;
      
          public User(int id, int age) {
              this.id = id;
              this.age = age;
          }
      
          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;
          }
      
          @Override
          public String toString() {
              return "\nUser{" +
                      "id=" + id +
                      ", age=" + age +
                      '}';
          }
      }
      

      你可能感兴趣的:(javaSE,java,intellij-idea)