泛型和Comparable、Comparator接口

♥泛型类型的本质:1. 能够检测参数的类型是否正确合法       2.自动类型转换,避免用户手动类型转换

♥java泛型类型的擦除机制:在编译阶段,能够检测参数的类型是否正确合法 ,自动类型转换,避免用户手动类型转换,类型就擦除到它的上界.

 

例:开始的Object类编程:

public class GenericTest {
    //定义一个存储数据的数组
    private Object[] array;
    //定义变量描述数组有效数据的大小
    private int size;

    public GenericTest(int size){
        this.array=new Object[size];
        this.size=0;
    }
    public GenericTest(){
        this(10);
    }
    //在数组尾部增加元素
    public void add(Object data){
        if(this.size==this.array.length){
            this.array= Arrays.copyOf(this.array,this.array.length*3);
        }
        this.array[size++]=data;
    }
    //获取数组最后一个元素
    public Object get(){
        if(this.size==0){
            return null;//引用类型默认返回null,也可以选择跑出异常
        }
        return this.array[size-1];//通过返回值返回
    }

    /**
     * Object写泛型编程提供的限制是java提供泛型编程本质的意义
     * @param args
     */
    public static void main(String[] args) {
        GenericTest g=new GenericTest();
        g.add(20);
        g.add(10);
        g.add("30");//Object可以接受任意类型,所以本来想存数组,结果写错存了字符串"50"
        //g.add("hello world");//不会报错,可以接受任意类型
        int data=(int)g.get();//基类类型不能赋给具体类型(不能把大类型赋给小类型),会报错,所以要进行类型转化
        
        System.out.println(data);
    }

}

出现错误:类型转换错误,String类不能转化为Integer类型

 

 Object实现泛型编程的缺陷

  1.   g.add(20);
        g.add(10);
        g.add("30");  //这里无法检测传入的数据是否符合用户存储数据的正确类型,不能做类型检查

2.因为是用Object进行泛型编程,所有的方法返回值类型都是Object,  Object作为积累不能转化为派生类类型,必须手动加入类型转换

 int data=(int)g.get();

引出泛型的意义可以进行类型的检测和自动转换。

说明泛型类:普通类+<类型参数/类型占位符>    public class GenericTest2<T>

 

 

java泛型的好处:1.可以进行类型的检查  2.可以做类型自动转换

 

可以确认具体的类型也可以使用原始类型,默认为Object类型

 

 ♥为什么会报错但是不强制转换为指定类型?

历史问题,如果指定,以前开发的代码会报错。因为java5以后才增加泛型,但是要让原来的类使用的时候又不能报错,所以不强制。

 ♥java泛型的类型擦除机制?

在编译阶段,java编译器通过泛型类型进行类型检查和自动类型转换,处理完成以后,就会把T类型一直往上擦除,直到上界(Object)

GenericTest2<Integer>  GenericTest2<Double>一个类型,里面的类型只是为了做对应的类型检查

 注:打印出来类型相同,经过编译阶段尖括号里面的类型不存在了,擦除了;都是GenericTest2类型,里面的类型只存在编译阶段,运行阶段不存在了。

 

•参数类型不能为简单类型,因为在编译阶段要进行类型检测和类型转换,处理完成以后要向上擦除到它的基类,简单类型不是类类型,没有基类。

 

 再写一个Student类的泛型,按照id号进行排序:

class Student {    
    private String name;
    private Integer id;
    public Student(Integer id,String name){
        this.name=name;
        this.id=id;
    }
    @Override
    public String toString() {
        String str = "id:" + id + " name:" + name;
        return str;
    }
   public void getName() {
this.name=name;
}
}
 
public class StudentTest {
public static void main(String[] args) {
Student[] stuArr=new Student[3];
stuArr[0]=new Student(1001,"zhang san");
stuArr[1]=new Student(1000,"li si");
stuArr[2]=new Student(1002,"wang wu");
Arrays.sort(stuArr);//给数组排序
//报错,因为这个时候java编译器不知道要给什么进行排序 转型错误(对象没有办法进行比较)
//错误类型:Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
//要实现泛型接口,才能比较
System.out.println(Arrays.toString(stuArr));//打印数组
 

错误分析:

报错,因为这个时候java编译器不知道要给什么进行排序   转型错误(对象没有办法进行比较)

错误类型Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
要实现泛型接口,才能比较

实现接口后:class Student implements Comparable<Student>{ //Comparable 接口要有<学生对象>,比较学生对象

id实现按照顺序打印:

 

 注:自定义的类型要支持比较,一定要实现Comparable<Student>接口,重写compareTo方法

第一种方式:再创建一个比较用户传入的名字类,需要实现Comparator接口,再重写compare()方法:

Sudent类里面需要添加getName方法:
class Student{
    /**
     * 外部访问name,需要调用get方法,String类型的getName,需要返回Name
     */
    public String getName() {
        return name;
    }
} 
public static void main(String[] args){
/**
*sort的重载函数,接受一个自定义的比较器,比较name
*/ Arrays.sort(stuArr,
new StudentCompareName()); System.out.println(Arrays.toString(stuArr));//打印数组 } /**
*注:这里的名字需要写成英文的,才能进行排序"zhang san" ,中文式的不能比较"张三"
*这是定义的一个普通类型 *比较学生名字的类实现Comparator接口,重写compare方法 */ class StudentCompareName implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return o1.getName().compareTo(o2.getName()); } }

 

 第二种方式:定义一个实现了Comparator接口的匿名对象,不用再创建一个新类:

       /**
         * 用一个静态方法来实现名字的比较,产生匿名对象类,不用再创建新的类
        */
        Arrays.sort(stuArr,NAME_COPM);
        System.out.println(Arrays.toString(stuArr));//打印数组
   /**
     * new Comparator<Student>() 不是new实例化接口,而是实现了一个实现了Comparator接口的匿名对象而已
     */
    public static Comparator<Student> NAME_COPM=new Comparator<Student>() {
        @Override
        public int compare(Student o1, Student o2) {
            return o1.getName().compareTo(o2.getName());
        }
    };

 

 第三种方式:直接将匿名对象放在sort方法里(最简单)

 Arrays.sort(stuArr, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        System.out.println(Arrays.toString(stuArr));//打印数组

 

 输出一个泛型的顺序栈:实现出栈、入栈、判断栈空栈满

 

 /**
 *输出一个泛型的顺序栈
 * 实现构造函数 入栈出栈栈空栈满 获取栈顶元素peek
 * @param <T>
 */
public class GenericStack<T> {
    private T[] stack;
    private int top;

    public GenericStack() {
        this(4);
    }

    /**
     * 带参数的构造函数
     * @param size
     */
    public GenericStack(int size) {
        this.stack = (T[])new Object[size];
        this.top=0;
    }

    /**
     * 获取栈顶元素
     * @return
     */
    public T peek(){
        if(empty()){
            return null;
        }
        return this.stack[this.top-1];
    }

    /**  ctrl+Q  键生成说明
     * 入栈操作
     * @param data
     */
    public void push(T data){
        if(full()){
            this.stack=Arrays.copyOf(this.stack,this.stack.length*2);
        }
        this.stack[this.top++]=data;
    }
    public int size(){
        return this.top;
    }

    /**
     * 出栈并返回出栈元素
     * @return
     */
    public T pop(){
        if(empty()){
            return null;
        }
        T t= this.stack[this.top-1];
        --this.top;
        return t;
    }

    /**
     * 判断栈满
     * @return
     */
    private boolean full() { return this.top==this.stack.length; }

    /**
     * 判断栈空
     * @return
     */
    private boolean empty() { return this.top==0; }

    public static void main(String[] args)throws InterruptedException {
        GenericStack<String>  s=new GenericStack<>(10);
        s.push("20");
        s.push("10");
        System.out.println(s.pop());
        System.out.println(s.peek());

        for(int i=0;i<10;i++){
            s.push(String.valueOf(i+1));//将i+1转换为String类型的并入栈
        }
        while (!s.empty()){
            System.out.print(s.pop()+"  "); //将栈中的元素输出
        }

 

 

垃圾回收机制(GC):

GC:Garbage collection怎么回收对象-》任意一个对象,如果没有被其他引用变量引用,这个对象就可以被GC回收了
可以手动启动GC ,System.gc(),用jamp命令查看当前活着的进程

注:将引用变量s置为空后,查看进程时找不到该对象,说明已经被回收

 

 提供一个学生类,定义姓名、学号、年龄属性,提供get set方法

public class Student{
    private Integer id;
    private String name;
    private Integer age;

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

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class GenericStack<T> {
private T[] stack;
private int top;

public GenericStack() {
this(4);
}

/**
* 带参数的构造函数
* @param size
*/
public GenericStack(int size) {
this.stack = (T[])new Object[size];
this.top=0;
}

/**
* 获取栈顶元素
* @return
*/
public T peek(){
if(empty()){
return null;
}
return this.stack[this.top-1];
}

/** ctrl+Q 键生成说明
* 入栈操作
* @param data
*/
public void push(T data){
if(full()){
this.stack=Arrays.copyOf(this.stack,this.stack.length*2);
}
this.stack[this.top++]=data;
}
public int size(){
return this.top;
}

/**
* 出栈并返回出栈元素
* @return
*/
public T pop(){
if(empty()){
return null;
}
T t= this.stack[this.top-1];
--this.top;
this.stack[this.top]=null; //将出栈的值置为null,才能将对象删除
return t;
}

/**
* 判断栈满
* @return
*/
private boolean full() { return this.top==this.stack.length; }

/**
* 判断栈空
* @return
*/
private boolean empty() { return this.top==0; }
 
public static void main(String[] args)throws InterruptedException {
GenericStack<Student> s2=new GenericStack<>();
s2.push(new Student(1000,"zhangsan",20));
s2.push(new Student(1001,"lisi",21));
s2.push(new Student(1002,"wangwu",22));
s2.push(new Student(1003,"zhan",20));
s2.push(new Student(1004,"san",20));

System.out.println(s2.pop());
System.out.println(s2.pop());
Thread.sleep(10000000);
}
}
 

 没有将输出的栈元素置空时,在出栈两个元素后,查看Student依然存活着5个对象。

 

 

在出栈的方法中加入  this.stack[this.top]=null; //将出栈的值置为null,才能将对象删除,此时查看活着的进程,只有三个。

 

 将栈里面的元素放在一个数组中,并按照学号排序:

 /**
     * 想将目前栈里面的元素放在一个数组里(3个)
     * 所以返回栈元素的个数
     * @return
     */
    public int size(){
        return this.top;
    }
}
public static void main(String[] args){
Student[] st =new Student[s2.size()];
        for(int i=0;!s2.empty();++i){
            st[i]=s2.pop();
        }
        System.out.println(Arrays.toString(st));

        Arrays.sort(st);
        System.out.println(Arrays.toString(st));
       }
}

出现类型转型错误:

 

  给Student实现Comparable接口,重写它的compareTo方法,才能进行比较

 

 但是后面可能还有别的需求;难道比较名字、年龄就要重写compareTo方法吗?

sort函数提供了相应的构造器来接收比较的内容:λ表达式

 

 若按照从大到小排序:(a,b)->{ - return a.getAge().compareTo(b.getAge());}  (加负号即可)

 泛型方法:

1.普通泛型方法

在一个数组中寻找最大值

public T findMaxValue(T[] arr) {
        if(arr==null){
            return null;
        }
        if(arr.length==1){
            return arr[0];
        }
        T max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (max.compareTo(arr[i])<0) {       //compareTo比较对象,不能用 >  <
                max = arr[i];
            }
        }
      return max;
    }

2.静态的泛型方法

/**
     * static 方法能不能使用泛型类定义的泛型类型参数呢??   不能。静态方法的调用不需要依赖对象
     * java的泛型可以定义三种东西:1.泛型类public class A<T> { }
     * 2.泛型方法: 在函数的返回值前面增加类型列表
     * 3.泛型接口  Comparable Comparator
     * @param arr
     * @param <E>   可改变的方法的类型,要实现compareTo方法,该类需要继Comparable接口
     *     第二个E相当于其他方法里的void ,即返回一个E类型的参数
     * @return
     */
    public static  <E extends Comparable<E>>  E findMax (E[] arr){
        E max1=arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (max1.compareTo(arr[i])<0) {    //compareTo比较对象
                max1 = arr[i];
            }
        }return max1;
    }

总程序:

/**
 * 要求T类型向上擦除到Comparable才能实现compareTo方法
 * 此处,给泛型类型定义类泛型的上界(不定义上界,就擦除到Object类了)
 *现在擦除到接口类型
 * @param <T>
 *     2019/10/22
 */
class Compare<T extends Comparable<T>> {

    public T findMaxValue(T[] arr) {
        if(arr==null){
            return null;
        }
        if(arr.length==1){
            return arr[0];
        }
        T max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (max.compareTo(arr[i])<0) {       //compareTo比较对象,不能用 >  <
                max = arr[i];
            }
        }
      return max;
    }

    /**
     * static 方法能不能使用泛型类定义的泛型类型参数呢??   不能。静态方法的调用不需要依赖对象
     * java的泛型可以定义三种东西:1.泛型类public class A<T> { }
     * 2.泛型方法: 在函数的返回值前面增加类型列表
     * 3.泛型接口  Comparable Comparator
     * @param arr
     * @param <E>   可改变的方法的类型,要实现compareTo方法,该类需要继Comparable接口
     *     第二个E相当于其他方法里的void ,即返回一个E类型的参数
     * @return
     */
    public static  <E extends Comparable<E>>  E findMax (E[] arr){
        E max1=arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (max1.compareTo(arr[i])<0) {    //compareTo比较对象
                max1 = arr[i];
            }
        }return max1;
    }
}
public class 泛型的上界 {
    public static void main(String[] args) {
        Integer[] arr=new Integer[]{12,5,78,9,45};
        Compare<Integer> comp=new Compare();
        System.out.println(comp.findMaxValue(arr));

       System.out.println(Compare.findMax(arr));
        }
    }

 

 ♥总结:

泛型可以用来定义什么?

1.泛型类 public class A<T>{  }

2.泛型方法 (1).普通方法:public T fly(T  t)  { }  (若要实现比较应用compareTo方法,该类需要继承Comparable接口,需要给泛型定义上界)

                   (2).静态方法:public <E> E fly(E  e)(若要实现比较应用compareTo方法,该类需要继承Comparable接口<E extends Comparble> )

3.泛型接口   class StudentCompareName implements Comparator<Student>  ,比较的是Student里面的属性,所以<Student>

                       Comparble(重写compareTo方法)     Comparator(重写compare方法,两个参数)

 

 

 

 

 

 



posted @ 2019-10-21 20:32  acehm  阅读(1514)  评论(0)    收藏  举报