Java-什么是泛型,为什么使用泛型,集合中的泛型使用,自定义泛型类,泛型方法,泛型的使用场景,泛型在继承方面的体现,无边界通配符,上界通配符,下界通配符,泛型的约束与局限性

目录

  • 什么是泛型?
  • 为什么使用泛型?
  • 集合中的泛型使用
  • 自定义泛型类
  • 泛型方法
  • 泛型的使用场景
  • 泛型在继承方面的体现
  • 无边界通配符:
  • 上界通配符:
  • 下界通配符:
  • 泛型的约束与局限性

什么是泛型?

    所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如继承实现接口,用这个类型声明变量、创建对象时)确定(既传入实际的类型参数,也称为类型实参)。
    JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型参数。(List 表明该list只能保存字符串类型的对象)
    如果没有指定泛型的类型,默认类型为java.lang.Object类型。

为什么使用泛型?

  1. 集合中没有泛型之前任何元素都可以添加到集合中,会出现类型不安全。
  2. 强制转换时会出现ClassCastException
	ArrayList list=new ArrayList();
	list.add(10);
	list.add(20);
	list.add(30);
	list.add(40);
	list.add("awsl");   //类型不安全
	for (Object object : list) {
		int number = (int) object;   //异常
		System.out.println(number);
	}

集合中的泛型使用

	ArrayList<Integer> list=new ArrayList<>();
	list.add(10);
	list.add(20);
	list.add(30);
	list.add(40);
	for (int number : list) {
		System.out.println(number);
	}
	Map<String, Integer> map=new HashMap<String, Integer>();
	map.put("Tom", 10);
	map.put("Jerry", 20);
	map.put("Mark", 30);
	//泛型嵌套
	Set<Map.Entry<String, Integer>> entry=map.entrySet();
	Iterator<Map.Entry<String, Integer>> iterator=entry.iterator();
	while (iterator.hasNext()) {
		Map.Entry<String, Integer> e=iterator.next();
		String key=e.getKey();
		int number=e.getValue();
		System.out.println(key+": "+number);
	}

自定义泛型类

public class Study {
	static class Pair<T>{
		private T first;
		private T second;
		
		public Pair() { this.first=null; this.second=null; }
		public Pair(T first,T second) { this.first=first; this.second=second; }
		public T getFirst() { return first; }
		public T getSecond() { return second; }
		public void setFirst(T first) { this.first = first; }
		public void setSecond(T second) { this.second = second; }
	}
	
	public static Pair<String> minmax(String[] a) {
		if(a==null||a.length==0) return null;
		String min=a[0];
		String max=a[0];
		for(int i=1;i<a.length;i++) {
			if(min.compareTo(a[i])>0) min=a[i];
			if(max.compareTo(a[i])<0) max=a[i];
		}
		return new Pair<>(min,max);
	}
	
	public static void main(String[] args) {
		String[] word= {"Mary","had","a","little","lamb"};
		Pair<String> mm=minmax(word);
		System.out.println("min= "+mm.getFirst());
		System.out.println("max= "+mm.getSecond());
	}
}

运行结果:
min= Mary
max= little

    当子类在继承带泛型的父类时,由于父类指定了泛型类型,则实例化子类对象时,子类不需要在声明泛型。

	public class Sub extends Parent<Integer>{}
	
	Sub sub=new Sub();

    但是子类也可以定义为泛型:

	public class Sub<T> extends Parent<T>{}
	
	Sub<String> sub=new Sub<>();

泛型方法

    泛型方法可以定义在普通类中,也可以定义在泛型类中(泛型方法的泛型参数与类的泛型参数没有任何关系)。
    注意:public T getFirst() { return first; } 这些不是泛型方法。

public class Test<T>{
	public <E> List<E> copyFromArrayToList(E[] array) {   //注意返回值前面有一个类型参数
		ArrayList<E> list = new ArrayList<E>();
		for(E e : array) {
			list.add(e);
		}
		return list;
	}

	public static void main(String[] args) {
		Test<String> test=new Test<>();
		Integer[] array = new Integer[] {1,2,3,4};
		List<Integer> list = test.copyFromArrayToList(array);
		System.out.println(list);
	}
}

运行结果:[1, 2, 3, 4]

    泛型方法可以声明为 static 静态的,原因:泛型参数是在调用方法时确定的,并非在实例化类时确定的。
    有时,类或方法需要对类型变量加以约束,可以通过对类型变量E设置限定(bound)实现这一点:public List copyFromArrayToList(E[] array)

泛型的使用场景

    假如我们需要对数据库进行操作,那么我们有一个DAO类来存放数据库的基本的增删改查操作。

public class DAO<T> {
	//添加一条记录
	public void add(T t) {...}
	//删除一条记录
	public boolean remove(T t) {...}
	//修改一条记录
	public void update(int index,T t) {...}
	//查询一条记录
	public T search(int index) {...}
}

    同时,我们有一个客户类Customer

public class Customer {
	String name;
	int number;
	
	public Customer () { this.name=null; this.number=null; }
	public Customer (String name,int number) { this.name=name; this.number=number; }
	public String getName() { return name; }
	public int getNumber() { return number; }
	public void setName(String name) { this.name= name; }
	public void setNumber(int number) { this.number= number; }
}

    我们还有一个专门对客户表进行操作的类

public class CustomerDAO extends DAO<Customer> {...}

    现在,我们可以在其他地方对客户表操作

public class Test {
	CustomerDAO dao = new CustomerDAO();
	dao.add(new Cumstomer());
	List<Customer> list = dao.search(10);
}

泛型在继承方面的体现

    如果类A是类B的父类,G 和 G 二者不具备子父类关系,二者是并列关系。

	Object object = null;
	String stirng = null;
	object = string;   //正确
	
	List<Object> list1 = null;
	List<String> list2 = null;
	list1 = list2;   //错误

    相反,如果类A是类B的父类,A 是 B 的父类。

	List<String> list1 = null;
	ArrayList<String> list2 = null;
	list1 = list2;   //子父类关系

无边界通配符:

    无边界的通配符的主要作用就是让泛型能够接受未知类型的数据

public class Test<T>{
	public static void print(List<?> list) {
	//对于List不能添加数据(除了null)
	//Object是类的父类,并且不会报错
		for (Object object : list) {
			System.out.print(object);
		}
	}

	public static void main(String[] args) {
		List<Integer> list1 = Arrays.asList(new Integer[] {1,2,3,4});
		List<String> list2 = Arrays.asList(new String[] {"Tom","Jerry","Marry","Tong"});
		
		System.out.println(list1);
		System.out.println(list2);
	}
}

上界通配符:

    在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类。
    G 可以作为 G
和G的父类,其中B是A的子类 。

下界通配符:

    在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。
    G 可以作为 G
和G的父类,其中B是A的父类 。

泛型的约束与局限性

  1. 不能用基本类型实例化类型参数
        不能用类型参数代替基本类型。因此,没有Pair, 只有Pair 。其原因是类型擦除。擦除之后,Pair类含有Object类型的域,而Object不能存储double值。
  2. 运行时类型查询只适用于原始类型
        虚拟机中的对象总有一个特定的非泛型类型。因此,所有的类型查询只产生原始类型。试图查询一个对象是否属于某个泛型类型时,倘若使用instanceof会得到一个编译器错误,如果使用强制类型转换会得到一个警告。
        if (a instanceof Pair) // Error
        if (a instanceof Pair) // Error
        同样的道理,getClass方法总是返回原始类型。
        Pair stringPair = . . .
        Pair employeePair = . . .
        if (stringPair.getClass() == employeePair.getClass()) // they are equal
        其比较的结果是 true, 这是因为两次调用 getClass 都将返回 Pair.class。
  3. 不能创建参数化类型的数组,Java不支持泛型类型的数组
        Pair[] table = new Pair[10]; // Error
        擦除之后,table的类型是Pair[],可以把它转换为 Object[]:
        Object[] objarray = table;
  4. 不能实例化类型变量。不能构造泛型数组。不能使用像new T(…),new T[…]或T.class这样的表达式中的类型变量
  5. 不能在静态域或静态方法中引用类型变量。禁止使用带有类型变量的静态域和静态方法。
        public class Singleton{
            private static T singlelnstance; // Error
            public static T getSinglelnstance(){ // Error
            if (singleinstance == null)
                construct new instance of T
                return singlelnstance;
            }
        }
  6. 不能抛出或捕获泛型类的实例。泛型类不能继承异常类

你可能感兴趣的:(Java学习)