• Java基础教程——泛型


    泛型

    Generics:泛型,愿意指“无商标的”。

    泛型,可以理解为“宽泛的数据类型”,就是将类型由原来的具体的类型泛化。

    泛型在建立对象时不指定类中属性的具体类型,而是在声明及实例化对象时由外部指定。泛型可以提高数据安全性。

    List中应用了泛型,在编译期对数据类型进行严格 检查,如果类型不匹配,编译无法通过。
    示例 :

    public interface List<E> extends Collection<E>
    

    E:Element

    T:Type


    泛型的本质是为了参数化类型,即在不创建新类型的情况下,通过泛型指定的不同类型(类型形参),调用时传入具体的类型(类型实参)。

    在使用泛型过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

    泛型类

    public class 泛型_类 {
    	public static void main(String[] args) {
    		车<Girl> v = new 车<Girl>();
    		v.add(new Girl());
    		// 不让上车
    		// v.add(new Boy());
    	}
    }
    abstract class Person {
    }
    class Boy extends Person {
    }
    class Girl extends Person {
    }
    class 车<T> {
    	public T add(T arg) {
    		System.out.println(arg.getClass());
    		return arg;
    	}
    }
    

    泛型接口

    泛型接口与泛型类的定义及使用基本相同。

    public interface Generator<T> {
        public T getS();
    }
    

    泛型接口的两种使用方法(以ArrayList和Scanner为例):

    import java.util.*;
    interface IGenerics<E> {
    	void m(E e);
    }
    // 仿照ArrayList<E>,implements List<E>
    // 实现类还是泛型
    class MyList<E> implements IGenerics<E> {
    	@Override
    	public void m(E e) {
    		List<E> lst = new ArrayList<>();
    	}
    }
    // 仿Scanner类, implements Iterator<String>
    // 直接指定泛型为String
    class MyScanner implements IGenerics<String> {
    	@Override
    	public void m(String e) {
    		Scanner sc = new Scanner(System.in);
    	}
    }
    public class 泛型接口 {
    	public static void main(String[] args) {
    		// 定义时未指定类型,实例化需要指定类型
    		MyList<Integer> my1 = new MyList<>();
    		my1.m(1);
    		// 定义时已经指定类型,实例化不用指定
    		MyScanner my2 = new MyScanner();
    		my2.m("A");
    	}
    }
    

    泛型方法

    泛型定义在修饰符和返回类型之间,在参数列表中使用泛型。
    泛型方法主要针对参数有泛型(如集合)的场景,如果把下例的参数List改为数组则和Object没有太大区别

    import java.util.*;
    public class 泛型方法 {
    	public static <T> int find(List<T> lst, T a) {
    		int indexOf = -1;
    		if (lst != null) {
    			indexOf = lst.indexOf(a);
    		}
    		return indexOf;
    	}
    	public static void main(String[] args) {
    		int find = find(new ArrayList<String>(), "A");
    		System.out.println(find);
    		// 编译不通过:find(new ArrayList<Integer>(), "A");
    	}
    }
    

    说明:

    泛型的参数类型只能是类类型,不能是简单类型。比如,<int>是不可使用的。
    可以声明多个泛型参数类型,比如<T,P,Q…>,同时还可以嵌套泛型,例如:<List<String>>。
    泛型的参数类型可以使用extends语句,例如<T extends superclass>。
    使用extends语句将限制泛型参数的适用范围,只能是指定类型的子类。
    |-例如:<T extends Collection> ,则表示该泛型参数的使用范围是所有实现了Collection接口的calss。如果传入<String>则程序编译出错。
    

    示例【T extends Collection】:

    import java.util.*;
    public class 泛型extends {
    	public static <A extends Collection> void find(A arg1, int arg2) {
    	}
    	public static void main(String[] args) {
    		find(new ArrayList(), 5);
    		// 不是Collection的子类,编译不通过:
    		// find(new String(), 5);
    	}
    }
    

    <?>通配符

    用于承接不同类型的泛型对象。

    不知道使用什么类型接收数据时,使用?通配符。

    此时只能接收数据,不能往该集合中存储数据。

    import java.util.*;
    public class 泛型_通配符 {
    	public static void main(String[] args) {
    		List<String> lst1 = new ArrayList<String>();
    		lst1.add("A");
    		List<Integer> lst2 = new ArrayList<Integer>();
    		lst2.add(1);
    		printList_T(lst1);
    		printList_T(lst2);
    		System.out.println("----------");
    		printList(lst1);
    		printList(lst2);
    	}
    	// 用常规泛型写,比较麻烦
    	private static <T> void printList_T(List<T> list) {
    		list.add(list.get(0));
    		for (int i = 0; i < list.size(); i++) {
    			Object o = list.get(i);
    			System.out.println(o);
    		}
    	}
    	// 用泛型通配符写简单
    	private static void printList(List<?> list) {
    		// ?不能写入,此处编译错误:list.add(list.get(0));
    		for (int i = 0; i < list.size(); i++) {
    			Object o = list.get(i);
    			System.out.println(o);
    		}
    	}
    }
    

    通配符界限

    <? extends T>:是 “上界通配符(Upper Bounds Wildcards)”,泛型只能是T的子类/本身
    <? super T>:是 “下界通配符(Lower Bounds Wildcards)”,泛型只能是T的父类/本身
    参见:泛型通配符界限问题: https://www.cnblogs.com/tigerlion/p/10659515.html
    

    定义三个类,Fruit->Apple->RedApple,声明ArrayList,有如下现象:

    import java.util.*;
    public class 泛型_通配符边界2 {
    	public static void main(String[] args) {
    		ArrayList<Apple> p1 = new ArrayList<Apple>();
    		// 【震惊!装得了苹果,装不了红苹果】
    		// ↓Type mismatch:
    		// cannot convert from ArrayList<RedApple> to ArrayList<Apple>
    		// ArrayList<Apple> p2 = new ArrayList<RedApple>();
    	}
    }
    class Fruit {
    }
    class Apple extends Fruit {
    }
    class RedApple extends Apple {
    }
    

    解决方案就是

    【1.<? extends Fruit>,定类型继承关系的上界】
    【2.<? super Apple>,确定类型继承关系的下界】
    

    解决方案如下,照着注释看便是

    package ah;
    import java.util.*;
    // 先定义三个类:水果、苹果、红苹果
    class Fruit {
    }
    class Apple extends Fruit {
    }
    class RedApple extends Apple {
    }
    public class 泛型_通配符边界 {
    	public static void main(String[] args) {
    		ArrayList<Apple> p1 = new ArrayList<Apple>();
    		// 【震惊!装得了苹果,装不了红苹果】
    		// ↓Type mismatch:
    		// cannot convert from ArrayList<RedApple> to ArrayList<Apple>
    		// ArrayList<Apple> p2 = new ArrayList<RedApple>();
    		// 【1.<? extends Fruit>,定类型继承关系的上界:】
    		// 能装Apple以及一切Apple的派生类
    		ArrayList<? extends Apple> p3 = new ArrayList<Apple>();
    		p3 = new ArrayList<RedApple>();
    		// ↓上层的类不接受
    		// Type mismatch:
    		// cannot convert from ArrayList<Fruit> to ArrayList<? extends Apple>
    		// p3 = new ArrayList<Fruit>();
    		// 【然而,extends是只读的,不能写入】
    		// p3.add(new Apple());
    		if (p3.size() != 0) {
    			Apple apple = p3.get(0);
    		}
    		// 【2.<? super Apple>,确定类型继承关系的下界】
    		// 能装苹果以及一切苹果的基类
    		ArrayList<? super Apple> p4 = new ArrayList<Apple>();
    		p4 = new ArrayList<Fruit>();
    		p4 = new ArrayList<Apple>();
    		// Type mismatch:
    		// cannot convert from ArrayList<RedApple> to ArrayList<? super Apple>
    		// p4 = new ArrayList<RedApple>();
    		// 【可读可写,读出来的是Object类型】
    		p4.add(new RedApple());// 子类对象但是可以写进入,因为默认向上转型
    		Object object = p4.get(0);
    		System.out.println(object);
    		// 最后,其实JDK 7之后,后面的<>里什么都不用写
    		List<Apple> p2 = new ArrayList<>();
    	}
    }
    
  • 相关阅读:
    关于springMVC的一些常用注解
    关于springMVC的一些xml配置
    关于easyUI的一些js方法
    关于easyUI一些标签的使用
    关于easyUI分页
    P3376 模板网络流
    P1343 地震逃生
    网络流最大流
    python函数知识三 函数名的使用、格式化、递归
    python函数知识二 动态参数、函数的注释、名称空间、函数的嵌套、global,nonlocal
  • 原文地址:https://www.cnblogs.com/tigerlion/p/11179214.html
Copyright © 2020-2023  润新知