泛型

首先贴上甲骨文官网对泛型的解释

可以使用泛型开发一个更好的解决方案,在实例化时为所使用的容器分配一个类型,也称泛型类型,这样就可以创建一个对象来存储所分配类型的对象。泛型类型是一种类型参数化的类或接口,这意味着可以通过执行泛型类型调用 分配一个类型,将用分配的具体类型替换泛型类型。然后,所分配的类型将用于限制容器内使用的值,这样就无需进行类型转换,还可以在编译时提供更强的类型检查。

大意就是进行"参数化类型",在类,接口,方法的定义上都可以使用,用来指定数据类型名,编译器在进行编译的时候检查传入数据的类型,免得在运行的时候出现大量的错误.需要注意的是,泛型只能传入引用数据类型,不能传入基本数据类型.

 然后来看一个不用泛型与用泛型存储多种类型数据的对比实例,可能会对以上的内容有一个了解.

不使用泛型:

首先定义一个简单的Point类型:

/*一个简单的Point类,只提供构造器,get方法与toString方法*/
public class Point {
    private int x;private int y;
    /*构造器*/
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    /*get方法*/
    public int getX() { return x; }
    public int getY() { return y; }
    /*toString方法*/
    public String toString() {
        return "("+x+","+y+")";
    }
}

然后提供容器类型Cotain类型,提供一个能存储任何类型数据的Object类型数组作为容器:

import java.util.Arrays;
/*容器类型*/
public class Contain {
    /*想要存储各种类型数据,就必须要用Object类型的"容器"了.*/
    private Object[] objs;
    public  Contain(){
        /*初始化容器*/
        objs=new Object[0];
    }
    /*向容器添加元素的方法*/
    public void add(Object o){
        /*扩容以存储数据*/
        objs=Arrays.copyOf(objs,objs.length+1);
        /*数据添加至容器*/
        objs[objs.length-1]=o;
    }
    /*通过下标取出元素*/
    public Object get(int index){
        if (index>0&&index<objs.length)
            return objs[index];
        else {
            throw new RuntimeException("传入参数有误.");
        }
    }
}

 测试类TestContain:

public class TestContain {
    public static void main(String[] args) {
        /*初始化容器类型*/
        Contain c=new Contain();
        /*存入String类型数据*/
        c.add("String类型数据1");
        c.add("String类型数据2");
        /*存入Integer类型数据*/
        c.add(15);//发生了自动装箱
        /*存入Point类型数据*/
        c.add(new Point(5,6));
        c.add(new Point(11,15));
        /*取出一个String类型数据,必须进行强制类型转换*/
        String str= (String) c.get(1);
        /*取出一个Integer类型数据,必须进行强制类型转换*/
        Integer i= (Integer) c.get(2);
        /*取出一个Point类型数据,必须进行强制类型转换*/
        Point p= (Point) c.get(3);
        System.out.println(str);
        System.out.println(i);
        System.out.println(p);
    }
}

运行结果:

String类型数据2
15
(5,6)

可以看到取出三种类型的数据的数据要进行三次强制类型转换,十分繁琐. 

下面来看看使用泛型的情况,Point类直接使用.

提供泛型的类型Type,可以看到类名后使用尖括号定义了泛型.

/*泛型类型,尖括号内的字母为泛型,可以使用任意大写字母.*/
public class Type<T> {
    private T t;
    public Type(T t) { this.t = t; }
}

测试类TestType,定义了一个存储String类型数据的集合c:

import java.util.ArrayList;
import java.util.Collection;

public class TestType {
    public static void main(String[] args) {
        Type<String> c1=new Type<String>("A");
        Type<Integer> c2=new Type<Integer>(3);
        Type<Point> c3=new Type<Point>(new Point(2,2));
        /*定义存储String类型的集合c*/
        Collection<Type<String>> c=new ArrayList<>();
        c.add(c1);
    }
}

值得注意的是,调用ArrayList的add方法时,编译器自动检查了类型,使得非String类型的数据无法传入:

强行传入的情况:

可以看到编译器帮助我们检查了类型,防止传入错误类型的数据,有效避免了运行时出现的种种问题. 

List内元素的排序

首先定义一个Integer类型的集合并输出:

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

public class CompareTest {
    public static void main(String[] args) {
        List<Integer> list=new ArrayList<Integer>();
        list.add(10);
        list.add(7);
        list.add(19);
        list.add(2);
        list.add(8);
         System.out.println("排序之前的价格:"+list);
   ......

然后调用工具类Collections的sort方法进行排序并输出:

 .......
Collections.sort(list);
 System.out.println("排序之后的集合"+list);

运行结果:

排序之前的集合[10, 7, 19, 2, 8]
排序之后的集合[2, 7, 8, 10, 19]

可以看到是按照从小到大的顺序排的. 

再定义一个String类型的集合并做如上处理:

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

public class CompareTest {
    public static void main(String[] args) {
        List<String> list=new ArrayList<String>();
        list.add("Java");
        list.add("C");
        list.add("12345");
        list.add("Python");
        list.add("abcde");
        list.add("Perl");
        System.out.println("排序之前的集合"+list);
        Collections.sort(list);
        System.out.println("排序之后的集合"+list);
    }
}

运行结果: 

排序之前的集合[Java, C, 12345, Python, abcde, Perl]
排序之后的集合[12345, C, Java, Perl, Python, abcde]

可以看到是按照第一个字符的ASCII顺序排列的,若第一个字符一样则按照下一个的ASCII,以此类推. 

下面再看看上面定义的Point类能否进行排序:

刚刚把泛型改为Point就报错了,大概意思就是这个类未实现Comparable接口.

 而Integer类与String类都实现了该接口,即重写了compareTo方法(具体可以去源码查看),所以可以进行排序操作.

下面来看看这个方法的规则,首先看看Comparable接口的此方法:

public int compareTo(T o);

返回值为int,排序的关键就是这个int返回值了,下面再来看看Integer类的此方法:

public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }
/*再进入compare方法*/
public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }

按照以上的逻辑,compare方法的int x对应compareTo方法的this.value,int y对应anotherInteger.value. 

而Integer类型的集合是按照从小到大排列的,所以可以得出一个结论:

this的值减去o的值,按照升序排列.那么相反,o的值减去this的值就是降序了.

确定了顺序,就应该利用这个int返回值了:

如果大于0,返回大于0的一个数,this就排在o的后面.
如果等于0,   返回0.
如果小于0,  返回小于0的一个数,this就排在o的前面.

知道了排序规则之后,就可以尝试编写Point类的compareTo方法了,这里设定比较规则为距离原点的距离:

public class Point implements Comparable<Point>{
                                        /*↑注意添加泛型*/
......
  public int compareTo(Point o) {
        return (this.x*this.x+this.y*this.y)-(o.x*o.x+o.y*o.y);
    }
}

然后在测试类内添加五个点,这里使用随机数配合for循环生成:

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

public class CompareTest {
    public static void main(String[] args) {
        List<Point> list=new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            /*随机生成横纵坐标*/
            int x=(int)(Math.random()*20);
            int y=(int)(Math.random()*20);
            list.add(new Point(x,y));
        }
        System.out.println("排序之前的集合"+list);
        Collections.sort(list);
        System.out.println("排序之后的集合"+list);
    }
}

其中一次的运行结果为: 

排序之前的集合[(16,16), (6,12), (5,10), (8,15), (12,3)]
排序之后的集合[(5,10), (12,3), (6,12), (8,15), (16,16)]

可以看到五个点按照我们所规定的顺序排列了.

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐