java 泛型引入及详解,泛型通配符使用示例,JUnit 单元类测试框架

  • Post author:
  • Post category:java


一、引入

1、泛型是什么

泛型 ,顾名思义就是 广泛的数据类型,也就是说什么数据类型都可以。一般来说,我们见到的泛型就是这个样子,用 T 表示。如下所示,在类名后方申明泛型 T,接着就可以在成员变量、方法中使用泛型了。其中我们在java 集合构架中以及以后将会广泛的应用到泛型。

首先告诉大家ArrayList就是泛型之一。

首先让我们来看一个例子,认识一下泛型的作用之一。

在ArrayList 集合遍历中,我们又是在处理时将会用到泛型,但若保存了不同的对象,在遍历时就将报类型转换错误:

package fanxing.com;

import java.util.ArrayList;

@SuppressWarnings({"all"})
public class fanxingTest {
    public static void main(String[] args) {
        // 泛型:
        ArrayList<Dog> dogs = new ArrayList<Dog>();//  当我们这样写,表示ArrayList 集合中的元素只能是Dog 类型
        // 如果加入的是Cat那编译器就会报错
        dogs.add(new Dog("tom",6));
        dogs.add(new Dog("小黄",10));
        dogs.add(new Dog("招财",8));

        // 在遍历使用get 和 set 方法时不用在向下转型
        for (Dog dog :dogs) {
            System.out.println(dog.getName()+"-"+dog.getAge());
        }

    }
}


class Cat{
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

class Dog{
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

以上代码中,如果我们不使用泛型而同时存放了Cat 类对象和 Dog类对象,那在遍历时就将出错

那为了解决这个问题就可以参考以上代码,使用泛型,在生成ArrayList 对象时指定他所存储对象的类型。

另外泛型当然不仅仅只是为了解决这个问题啦,等到后面的题目时你就能感受到它的神奇之处了。


下面我们将对泛型的写法和用法做一一讲解。

二、各种泛型定义及使用

那么参照上一段代码,让我们来看看泛型可以怎样在集合中使用:

package fanxing.com;

import java.util.*;

@SuppressWarnings({"all"})
public class GenericTest01 {
    public static void main(String[] args) {
        // 使用泛型,在HashSet中放入三个学生对象
        HashSet<Student> students = new HashSet<>();
        students.add(new Student("jact",19));
        students.add(new Student("tom",21));
        students.add(new Student("jhon",18));

        Iterator<Student> iterator = students.iterator();
        while (iterator.hasNext()) {
            Student next =  iterator.next();
            System.out.println(next.getName()+"-"+next.getAge());
        }

        // 用HashMap 存放 key 为name ,value 为学生对象
        HashMap<String, Student> map = new HashMap<>();
        map.put("jact",new Student("jact",19));
        map.put("tom",new Student("tom",23));
        map.put("majunyi",new Student("majunyi",20));

        Set<Map.Entry<String, Student>> entries = map.entrySet();
        Iterator<Map.Entry<String, Student>> iterator1 = entries.iterator();
        while (iterator1.hasNext()) {
            Map.Entry<String, Student> next =  iterator1.next();
            Student value = next.getValue();
            System.out.println(value.getName()+"-"+value.getAge());
        }
    }
}


class Student{
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

另外在对进一步了解之前让我们看看泛型的一些注意事项吧:

package fanxing.com;

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

// 泛型使用注意事项
@SuppressWarnings({"all"})
public class GenericTest02 {
    public static void main(String[] args) {
        // 注意: E 不能是基本数据类型,比如 int、float、double 等
        // 1. 在给泛型指定具体类型以后,可以传入该类型或其子类对象
        Pig<A> aPig = new Pig<A>(new A());
        aPig.f();    // class fanxing.com.A
        Pig<A> aPig1 = new Pig<A>(new B());
        aPig1.f();   // class fanxing.com.B

        // 2.泛型的使用形式
        List<Integer> list1 = new ArrayList<Integer>();
        // 在实际开发中可以简写,并且更为推荐下面一种写法
        List<Integer> list2 = new ArrayList<>();
        ArrayList<Pig> pigs = new ArrayList<>();

        // 3. 如果是这样写 泛型默认是 Object 类
        ArrayList arrayList = new ArrayList();//等价于 ArrayList<Object> arrayList = new ArrayList<>();
        arrayList.add("majnuyi");//默认添加为 Object
        Tige tige = new Tige();
        /*  相当于:
        class Tige{
               Object e;

            public Tige(){

            }

           public Tige(Object e) {
                 this.e = e;
           }
        }
         */
    }
}

class Tige<E>{
    E e;

    public Tige(){

    }

    public Tige(E e) {
        this.e = e;
    }
}

class A{}
class B extends A{}

class Pig<E>{
    E e;

    public Pig(E e) {
        this.e = e;
    }

    public void f(){
        System.out.println(e.getClass());
    }
}

1.泛型接口:

package fanxing.com;

// 泛型接口
public class Generic_interface {
    public static void main(String[] args) {

    }
}

/**
 * 1. 接口中静态成员也不能使用泛型
 * 2. 泛型接口的类型,在继承接口或者实现接口时确定,如果没有指定默认Object 类
 */
interface Usb<U,R>{
    // 普通方法中,可以使用接口泛型
    R get(U u);

    void hi(R r);

    void run(R r1,R r2,U u1,U u2);

    // 在 jdk8 中,可以在接口中使用默认方法,也可以使用泛型
    default R method(U u){
        return null ;
    }
}

// 当我们去实现IA 接口时,因为 IA 在继承Usb 接口时指定了U 为String ,T 为 Double
// 在实现Usb 接口中方法时,使用String 代替 U,用 Double 代替 T
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) {

    }

    @Override
    public Double method(String s) {
        return IA.super.method(s);
    }
}

// 在继承接口时指定泛型
interface IA extends Usb<String,Double>{

}

/**
 * 在实现接口时指定泛型接口的类型
 * 给 U指定了 Integer, 给 T 指定了 Float
  */
class BB implements Usb<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) {

    }

    @Override
    public Float method(Integer integer) {
        return Usb.super.method(integer);
    }
}

/** 不指定时默认为Object 类
class CC implements Usb{// 等价于 给 U 和 T 指定了 Object,不建议这种写法,建议下面的写法
}
 */
class CC implements Usb<Object,Object>{

    @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) {

    }

    @Override
    public Object method(Object o) {
        return Usb.super.method(o);
    }
}

2.泛型方法:

package fanxing.com;


// 泛型方法
public class Generic_main {
    public static void main(String[] args) {
        Car car = new Car();
        car.fly("majnuyi",5784);// 调用时传入参数,自动装箱,编译器,就会确定类型
        car.fly(845,"546457");
        Fish<String, Double> fish = new Fish<>();
        System.out.println(fish.hi("zhanguhgds"));
    }
}



// 泛型方法可以放在普通类中,也可以放在泛型类中
class Car{// 普通类
    public void run(){

    }
    // 说明泛型方法
    // 1. <T,R> 就是泛型
    // 2. 是提供给fly使用
    public <T,R> void fly(T t,R r){// 泛型方法
        System.out.println(t+"-"+r);
    }

}

class Fish<T,R>{
    T t;
    R r;
    public void run(){

    }
    // 泛型方法
    public <U,M> void eat(U u,M m){

    }
    /*
    注意:
    下面方法只是使用了泛型,并不是泛型方法
    是 hi方法使用了类声明的泛型
    另外 泛型方法不仅可以使用类声明的泛型,还可以使用泛型方法中声明的泛型
    并且,普通方法使用泛型,必须是类声明的泛型
     */
    public T hi(T t){
        return t;
    }
}

3.自定义泛型类:

package fanxing.com;
//   自定义泛型
@SuppressWarnings({"all"})
public class GenericTest04 {
    public static void main(String[] args) {
        Tiges<Double, String, Integer> Tige = new Tiges<Double, String, Integer>("jhon");
        Tige.setT(10.87);
        Tige.setM(68);
        Tige.setR("majunyi");
        System.out.println(Tige); // 输出 Tiges{name='jhon', r=majunyi, m=68, t=10.87}

        // 以下代码也对,默认Object 类
        Tiges tiges = new Tiges("tom");
        tiges.setR("bfhjdrgh");
        tiges.setT("zhang");
        tiges.setM(3568);
        System.out.println(tiges);//  输出 Tiges{name='tom', r=bfhjdrgh, m=3568, t=zhang}
    }
}

// 1. Tiges 后面有泛型,所以我们将 Tiges 称为泛型类
// 2. T、R、M为泛型标识符,一般使用大写字母
// 3. 泛型标识符可以有多个
// 4. 普通成员可以是泛型(方法、属性)
// 5. 使用泛型数组时不能进行初始化
// 6. 静态方法中不能使用类的泛型
class Tiges<T,R,M>{
    String name;
    R r;
    M m;
    T t;
    // T[] ts=new T[8]; 错误,因为数组类型无法确定下来,无法在内存中开辟空间
    public Tiges(String name) {
        this.name = name;
    }

    public Tiges( R r, M m, T t) {
        this.r = r;
        this.m = m;
        this.t = t;
    }
    /*
    因为静态是和类相关的,在类加载时,对象还没有被创建
    所以,如果静态方法和静态属性中定义了泛型,JVM 就无法完成初始化
    static R r2;
    public static void f(M m){

    }

     */
    public String getName() {
        return name;
    }

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

    public R getR() {
        return r;
    }

    public void setR(R r) {
        this.r = r;
    }

    public M getM() {
        return m;
    }

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

    public T getT() {
        return t;
    }

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

    @Override
    public String toString() {
        return "Tiges{" +
                "name='" + name + '\'' +
                ", r=" + r +
                ", m=" + m +
                ", t=" + t +
                '}';
    }
}

那么通过以上对泛型的了解,让我们来做一个间的泛型题目吧:

package fanxing.com;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;

// 泛型练习
@SuppressWarnings({"all"})
public class GenericTest03 {
    public static void main(String[] args) {
        /*
        定义Employee类,该类包括 private: neme,sal,birthday(MyDate)类,
        其中 birthday 为 MyDate类包括: year,month,day,要求:
       使用泛型 添加三个员工存入ArrayList 中,并调用sort 方法传入 Comparator 对象(使用泛型)
        先按照name 排序,name相同则按照日期先后
        最后遍历集合
         */
        ArrayList<Employee> employees = new ArrayList<>();
        employees.add(new Employee("tom",20000,new MyDate(2002,12,29)));
        employees.add(new Employee("jact",30000,new MyDate(2001,10,7)));
        employees.add(new Employee("tom",50000,new MyDate(2003,9,19)));

        System.out.println(employees+"\n\n\n");

        employees.sort(new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
               // 先对传入的参数判断
                if (!(o1 instanceof Employee) && !(o2 instanceof Employee)){
                    System.out.println("类型不匹配!!!");
                    return 0;
                }
                // 先比较名字  字典顺序
                int i = o1.getName().compareTo(o2.getName());
                // 如果 i!=0;则直接返回
                if (i!=0){
                    return i;
                }//  name相同则按照日期先后
                // ====因为下面是对日期的比较,代码较乱,不利于代码封装,因此 将日期比较交给 MyDate 类完成==
                // 封装后,将来的可复用性和可维护性将大大提升
               else {
                   return o1.getBrithday().compareTo(o2.getBrithday());
                }
            }
        });

        Iterator<Employee> iterator = employees.iterator();
        while (iterator.hasNext()) {
            Employee next =  iterator.next();
            System.out.println(next);
        }
    }
}


class Employee{
    private String name;
    private double Sal;
    private MyDate brithday;

    public Employee(String name, double sal, MyDate brithday) {
        this.name = name;
        Sal = sal;
        this.brithday = brithday;
    }

    public String getName() {
        return name;
    }

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

    public double getSal() {
        return Sal;
    }

    public void setSal(double sal) {
        Sal = sal;
    }

    public MyDate getBrithday() {
        return brithday;
    }

    public void setBrithday(MyDate brithday) {
        this.brithday = brithday;
    }

    @Override
    public String toString() {
        return "\nEmployee{" +
                "name='" + name + '\'' +
                ", Sal=" + Sal +
                ", brithday=" + brithday +
                '}';
    }
}

class MyDate implements Comparable<MyDate>{
    private int year;
    private int month;
    private int day;

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

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    @Override
    public String toString() {
        return "MyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }

    @Override
    public int compareTo(MyDate o) {// 把比较放在这里
            int yearMinus=year- o.getYear();
            // 继续判断
            if (yearMinus!=0){
                return yearMinus;
            }
            else {
                int monthMinus=month-o.getMonth();
                // 再判断
                if (monthMinus!=0){
                    return monthMinus;
                }
                else {
                    return day-o.getDay();
                }
            }
    }
}

其中,并调用sort 方法传入 Comparator 对象(使用泛型) 先按照name 排序,name相同则按照日期先后,关于Comparator的使用在我的另一篇文章(java 集合构架)中我有详细介绍.

4.泛型通配符使用:

package fanxing.com;


// 泛型继承和通配符

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

public class Generic_Extends {
    public static void main(String[] args) {
        Object o=new String("majuniy");// 没有问题
        System.out.println(o);
        // 泛型不具备继承性 以下使用错误
         //List<Object> list=new ArrayList<String>();

        /**
         * 泛型通配符
         *1.  <?> :支持任意泛型类型
         * 2. <? extends A> : 支持 A类以及A 类的子类,规定了泛型的上限
         * 3. <? super A> : 支持 A类以及A 类的父类,不限于直接父类,规定了泛型的下限 包括 Object 类
         */

        ArrayList<Object> list1 = new ArrayList<>();
        ArrayList<String> list2 = new ArrayList<>();
        ArrayList<X> list3 = new ArrayList<>();
        ArrayList<Y> list4 = new ArrayList<>();
        ArrayList<Z> list5 = new ArrayList<>();

        // 如果是  <?> :支持任意泛型类型
        printCollenction1(list1);
        printCollenction1(list2);
        printCollenction1(list3);
        printCollenction1(list4);
        printCollenction1(list5);

        // 如果是  <? extends X> : 支持 X类以及X 类的子类,规定了泛型的上限
       //  printCollenction2(list2); 错误使用
        printCollenction2(list3);
        printCollenction2(list4);
        printCollenction2(list5);

        // 如果是  <? super Z> : 支持 Z类以及Z 类的父类,不限于直接父类,规定了泛型的下限 包括 Object 类
        printCollenction3(list1);
        printCollenction3(list3);
        printCollenction3(list4);
        printCollenction3(list5);

    }
    // 编写几个方法:
    public static void printCollenction1(List<?> c){ // 支持任意泛型类型
        for (Object o :c) {
            System.out.println(o);
        }
    }

    public static void printCollenction2(List<? extends X> c){// 支持 X类以及X 类的子类,规定了泛型的上限
        for (Object o :c) {
            System.out.println(o);
        }
    }

    public static void printCollenction3(List<? super Z> c){
        // 支持 Z类以及Z 类的父类,不限于直接父类,规定了泛型的下限 包括 Object 类
        for (Object o :c) {
            System.out.println(o);
        }
    }
}


class X{

}

class Y extends X{

}

class Z extends Y{

}

5. JUnit 单元类测试框架:

关于java JUnit 测试类的使用我就为大家准备了IDEA操作截图

那首先让我们先来了解一下什么是 JUnit 测试框架呢:

平时我们调用方法都是:

public class GenericTest05 {
    public static void main(String[] args) {
        test1();
    }
    public static void test1(){
        System.out.println("调用成功!!!");
    }
}

而且该方法必须是静态方法才能被调用成功。

那来看看有了Junit 时:

有了JUnit 我们可以单独调用一个方法,并且它可以是非静态方法。

这样就可以大大减少我们在测试时对 main方法的修改,下面为大家展示 如何生成JUnit 测试框架:

1. 首先在要测试的方法前面写上 @Test

2. 然后点击报错灯(当然你也可以快捷键进入)选择将” JUnit 5.70”添加到类路径中,因为我已经配置过过了,因此在这里就没有显示,当然JUnit 4也是可以的,但现在更推荐使用JUnit 5.7,跟上技术的更新嘛。

3. 然后点击确定,稍微等待一下他进行配置完成以后就可以了,配置一次就可以了,以后如果还需要使用的话,输入 @Test 回车即可生成

好了,通过以上对泛型的学习呢,让我们来看看一道关于泛型较为系统的题吧:

package fanxing.com;



import org.junit.jupiter.api.Test;

import java.util.*;

// 泛型练习以及 JUnit 测试框架

/**
 * 要求:
 * 定义泛型类DAD<T> ,在其中定义一个Map成员变量,Map键为String类型,值为T类型
 * 分别创建以下方法:
 *1.  public void save(String id,T entity) 保存T类型对象到 Map 中
 * 2. public T get(String id) 从Map 中取出id 对应的对象
 *3.  public void update(String id,T entity) 替换map 中key 对应的 Value
 * 4. public List<T> list() 返回map 中的所有T对象并存入 List集合,ArrayList
 * 5. public void delete(String id){ // 删除id 对应的对象
 *
 *定义一个 User类该类有:
 *  private int id;
 *  private int age;
 *  private String name;
 *
 *  创建DAD对象,分别调用save、get、 update、list、delete 方法操作User 对象
 *使用JUnit 单元测试类进行测试
 */
public class GenericTest05 {
    public static void main(String[] args) {

    }

    @Test
    public void testList(){
        DAD<User> dao = new DAD<>();
        // 添加
        dao.save("001",new User(2002,19,"tom"));
        dao.save("002",new User(2001,20,"jact"));
        dao.save("003",new User(2003,18,"john"));

        List<User> list=dao.list();//输出看看T(User) 对象
        System.out.println(list);

        // 将"003" 改为
        dao.update("003",new User(2000,21,"风煞"));
        list=dao.list();
        System.out.println(list);

        // 删除 ”001“
        dao.delete("001");
        list=dao.list();
        System.out.println(list);

        // 单独获取"003"
        System.out.println(dao.get("003"));

        // 好了其他方法我就不一一演示了
    }
}

class User{
    private int id;
    private int age;
    private String name;

    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 String toString() {
        return "\nUser{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

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

    public void save(String id,T entity){// 保存T类型对象到 Map 中
        map.put(id, entity);
    }

    public T get(String id){// 从Map 中取出id 对应的对象
        return map.get(id);
    }

    public void update(String id,T entity){// 替换map 中key 对应的 Value
        if(map.containsKey(id)){// 先判断是否存在该对象
            map.put(id,entity);// 若存在添加即替换
        }
        else System.out.println("不存在需要替换的对象");
    }

    public List<T> list(){// 返回map 中的所有T对象并存入 List集合,ArrayList
        // 遍历Map
        List<T> list=new ArrayList<>();
        Set<Map.Entry<String, T>> entries = map.entrySet();
        Iterator<Map.Entry<String, T>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, T> next = iterator.next();
            list.add(next.getValue());
        }
        return list;
    }

    public void delete(String id){ // 删除id 对应的对象
        if(map.containsKey(id)){
            map.remove(id);
        }
        else System.out.println("不存在需要删除的对象");
    }
}

谢谢大家,有问题欢迎评论哦!!!



版权声明:本文为m0_62780707原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。