java泛型的理解
最近在看视频,看到比较经典的比大小问题。输入两个数,返回大的数,类型可以为int,long,float等。
通常的教程中用这个例子引入了构造函数以及重载的概念,在学习完泛型后,我想到能不能写一个泛型的方法,用以实现比较。
为了完成这个任务,我们首先需要了解一下泛型。
什么的泛型
泛型是java语言系统的一种扩展,支持创建可以按照类型进行参数化的类。
泛型的好处
泛型的好处也是显而易见的,
首先可以扩充代码的通用性,通过泛型可以使方法支持更多的类型。
泛型有助于增强类型安全,编译器可以对类型进行比较准确的解读。
泛型有助于减少类型强制转换,减少出错机会。
我们需要掌握泛型哪些用法
需要掌握泛型接口、泛型类、泛型类中泛型方法、非泛型类中泛型方法、类型通配符、类型通配符的上下限这几个用法。
泛型接口
泛型接口的一个比较好的例子是java.util.List.class,<E>中的E为类型形参,可以接收具体类型的实参。接口中出现E的地方,均为接收到的具体类型的实参。
package java.util; public abstract interface List<E> extends Collection<E> { public abstract int size(); public abstract boolean isEmpty(); public abstract boolean contains(Object paramObject); public abstract Iterator<E> iterator(); public abstract Object[] toArray(); public abstract <T> T[] toArray(T[] paramArrayOfT); public abstract boolean add(E paramE); public abstract boolean remove(Object paramObject); public abstract boolean containsAll(Collection<?> paramCollection); public abstract boolean addAll(Collection<? extends E> paramCollection); public abstract boolean addAll(int paramInt, Collection<? extends E> paramCollection); public abstract boolean removeAll(Collection<?> paramCollection); public abstract boolean retainAll(Collection<?> paramCollection); public abstract void clear(); public abstract boolean equals(Object paramObject); public abstract int hashCode(); public abstract E get(int paramInt); public abstract E set(int paramInt, E paramE); public abstract void add(int paramInt, E paramE); public abstract E remove(int paramInt); public abstract int indexOf(Object paramObject); public abstract int lastIndexOf(Object paramObject); public abstract ListIterator<E> listIterator(); public abstract ListIterator<E> listIterator(int paramInt); public abstract List<E> subList(int paramInt1, int paramInt2); }
泛型类
泛型类的定义方法类似于接口
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8683452581122892189L; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. */ private transient Object[] elementData; public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } public E set(int index, E element) { RangeCheck(index); E oldValue = (E) elementData[index]; elementData[index] = element; return oldValue; } }
泛型类中泛型方法
以上文中set方法为例,使用时,直接用E表示其代表的类型实参。
非泛型类中泛型方法
泛型的声明需要在方法修饰符之后,返回值之前。
class test{ public <T> void paly(T a){ System.out.println(a.getClass()); } }
类型通配符
通过下面的测试方法可以比较好的反应出类型通配符的使用方法:
Box.java
public class Box<T>{ private T box; public T getBox() { return box; } public void setBox(T box) { this.box = box; } public Box(T a) { setBox(a); } }
BoxMain.java
public class BoxMain { /** * @param args */ public static void main(String[] args) { Box<Integer> test1 = new Box<Integer>(111); Box<String> test2 = new Box<String>("aaa"); show3(test1); show4(test2); show1(test1); show1(test2); show2(test1); show2(test2); } public static <T> void show1(Box<T> a){ System.out.println(a.getBox()); } public static void show2(Box<?> a){ System.out.println(a.getBox()); } public static void show3(Box<Integer> a) { System.out.println(a.getBox()); } public static void show4(Box<String> a) { System.out.println(a.getBox()); } }
其中,show1方法是非泛型类中的泛型方法,show2用了类型通配符,show3和show4是什么都没有用的情况。可以看出,使用类型通配符可以取代show3和show4方法,比较方便。
类型通配符的上下限
在该使用类型通配符的地方,如果需要考虑其类型的安全,我们有时需要提出这样的限制:类型通配符代表的类型需要有一个限制,如,必须是Test或者Test的子类,必须是Test或者Test的父类等。
这里就用到了
? extends Test
? super Test
这两种写法,其中? extends Test表示必须是Test或者Test的子类,称为类型通配符的上限。
? super Test表示必须是Test或者Test的父类,称为类型通配符的下限。
至此,我们完成了泛型的学习,下面我们完成一开始提出的问题,用泛型比较大小。
ReturnBig.java
package com.fan; public class ReturnBig <T extends Comparable<T>>{ public T a; public T b; public T getA() { return a; } public void setA(T a) { this.a = a; } public T getB() { return b; } public void setB(T b) { this.b = b; } public int compareTo(T o) { return a.compareTo(o); } public ReturnBig(T a,T b){ setA(a); setB(b); } public T returnBigT(){ if (compareTo(b)>0) { return a; }else{ return b; } } }
FanMain.java
package com.fan; public class FanMain { public static void main(String[] args) { //比较大小相对而言限制的比较多一点,如果是判等,简单的多。 ReturnBig<Integer> returnBig = new ReturnBig<Integer>(11,22); System.out.println("the big num is:" + returnBig.returnBigT()); ReturnBig<Float> returnBig1 = new ReturnBig<Float>(11.11f,22.11f); System.out.println("the big num is:" + returnBig1.returnBigT()); } }
这里用到了Comparable接口,需要注意的是实现Comparable接口的类是有限的,随便两个对象的比较本身也是没有意义的事。只有特定类型的比较才有意义。