赞
踩
目录
- public class MyArrayList {
- private final int capacity = 10;
- private int usedSize;
-
- //Object[]数组能存储任何类型的元素
- private Object[] elem;
- public MyArrayList() {
- elem = new Object[capacity];
- }
-
- //增添元素
- public void add(Object val) {
- if(usedSize != capacity) {
- elem[usedSize++] = val;
- } else {
- System.out.println("容量溢出");
- }
- }
-
- //获取元素
- public Object get(int pos) {
- return elem[pos];
- }
-
- public static void main(String[] args) {
-
- //在自定义的集合中能够存储任何类型的元素,显然不是程序员想要的结果
- MyArrayList myArrayList = new MyArrayList();
-
- myArrayList.add("a");//字符串型
- myArrayList.add(1);//整型
- myArrayList.add(2.0);//double型
-
- //每次读取元素的时候都要强制类型转换
- String s = (String) myArrayList.get(0);
- Integer a = (Integer) myArrayList.get(1);
- }
- }
- class 泛型类名称<类型形参列表> {
- // 这里可以使用类型参数
- }
-
- class ClassName<T1, T2, ..., Tn> {}
-
- class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
- // 这里可以使用类型参数
- }
-
- class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
- // 可以只使用部分类型参数
- }
- public class MyArrayList1<T> {
- private final int capacity = 10;
- private int usedSize;
-
- private T[] elem;
- public MyArrayList1() {
- //报错,不能创建一个泛型数组
- //elem = new T[capacity];
-
- //不报错,但是存在问题,此问题留到下文讲擦除机制时讲
- elem = (T[]) new Object[capacity];
- }
-
- //真正正确的创建泛型数组的方式--运用反射
- public MyArrayList1(Class<T> clazz,int capacity) {
- elem = (T[]) Array.newInstance(clazz,capacity);
- }
-
- //增添数据
- public void add(T val) {
- if(usedSize != capacity) {
- elem[usedSize++] = val;
- } else {
- System.out.println("容量溢出");
- }
- }
-
- //读取数据
- public T get(int pos) {
- return elem[pos];
- }
-
- public static void main(String[] args) {
-
- //使用泛型后自动进行编译检查和类型转换
- MyArrayList1<String> myArrayList = new MyArrayList1<String>();
- myArrayList.add("a");
-
- //报错
- //myArrayList.add(1);
- //myArrayList.add(2.0);
-
- //读取元素时不需要强制类型转换
- String s = myArrayList.get(0);
- }
- }
泛型实现数据类型参数化,传入的数据类型必须是基本数据类型的包装类。
泛型类 < 类型实参 > 变量名 ; // 定义一个泛型类引用new 泛型类 < 类型实参 > ( 构造方法实参 ); // 实例化一个泛型类对象
- //指定一个泛型类引用泛型类的对象
-
- ArrayList<String> list = new ArrayList<String>();//后面的String可以省略
-
- //自定义一个交换数据类
- class Swap<T> {}
-
- public static void main(String[] args) {
- Swap<String> swap = new Swap<>();
- }
方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }
- //定义交换两个数据值的类
-
- class Swap<T> {
-
- //普通泛型方法
-
- //(形式一)
- public void swap(T[] array, int i, int j) {
- T tmp = array[i];
- array[i] = array[j];
- array[j] = tmp;
- }
-
- //(形式二)
- public <T> void swap(T[] array, int i, int j) {
- T tmp = array[i];
- array[i] = array[j];
- array[j] = tmp;
- }
-
- }
方法限定符 static <类型形参列表> 返回值类型 方法名称(形参列表) { ... }
- //泛型的静态方法
- class Swap {
-
- public static<T> void swap(T[] array, int i, int j) {
- T tmp = array[i];
- array[i] = array[j];
- array[j] = tmp;
- }
-
- }
注:泛型静态方法不依赖于对象,所以不在class类后面添加泛型
interface 泛型接口名<类型形参列表> {....}
- //在此用Comparable接口来举例
-
- //此接口适用于任何类型的比较
-
- interface Comparable<T> {
-
- public int compareTo(T o);
-
- }
class 泛型类名称<类型形参 extends 类型边界> { ... }
- //泛型上界为Number
- class MyArray<E extends Number> {
-
- public static void main(String[] args) {
-
- //创建对象时只能是Number或Number的子类
- MyArray<Integer> myArray1 = new MyArray<Integer>();
- MyArray<Double> myArray2 = new MyArray<Double>();
-
- //报错,String不是Number的子类
- MyArray<String> myArray = new MyArray<String>();
- }
-
- }
- //自定义一个比较类
-
- //泛型方法
- class Alg1<T extends Comparable<T>> {
-
- public T findMax(T[] array) {
- T max = array[0];
- for (T elem: array) {
- if(elem.compareTo(max) > 0)
- max = elem;
- }
- return max;
- }
-
- }
该代码块的上界是Comparable接口,当构造该类对象时,必须是实现了Comparable接口,基本数据类型的包装类都是实现了Comparable接口。
擦除机制是Java5用来实现泛型的技术。一般来说,在运行时阶段,Java编译器先执行类型检查,然后执行擦除或删除泛型信息。而具体化的泛型,与此正好相反。基于具体化泛型系统的类实现在运行时作为顶级实体,它在运行时保留了类型参数,而这给基于类型和反射性的语言提供了确定的操作。
不能创建泛型数组,会存在隐患,以下用代码进行演示。
- class MyArray<T> {
-
- private final int capacity = 10;
- private int usedSize;
-
- private T[] elem;
-
- public MyArrayList1() {
- //报错,不能创建一个泛型数组
- //elem = new T[capacity];
-
- //不报错,但是存在问题,编译的时候,替换为Object[]
- elem = (T[]) new Object[capacity];
- }
-
- //真正正确的创建泛型数组的方式--运用反射
- public MyArrayList1(Class<T> clazz,int capacity) {
- elem = (T[]) Array.newInstance(clazz,capacity);
- }
-
- public T getPos(int pos) {
- return this.array[pos];
- }
-
- public void setVal(int pos,T val) {
- this.array[pos] = val;
- }
-
- //编译时返回的类型是Object[]
- public T[] getArray() {
- return array;
- }
-
-
- public static void main(String[] args) {
- MyArray<Integer> myArray1 = new MyArray<>();
-
- //使用getArray()方法,返回的是bject[]类型的数组,不是不能直接用Integer[]来接受的
- //数组和泛型的区别是数组是在运行时检查错误,而泛型是在编译时检查错误,为此该语句段未报错
- Integer[] strings = myArray1.getArray();
- }
- }
数组和泛型之间的一个重要区别是它们如何强制执行类型检查。具体来说,数组在运行时存储和检查类型信息,泛型在编译时检查类型错误。
返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Double等等类型,运行的时候,直接转给Integer类型的数组,编译器认为是不安全的。
另外,即使对返回的数组进行强制类型转换为(Integer[])也不能改变其内部元素是其他的数据类型,运行时程序任然会报错。
- public class MyArrayList1<T> {
- private final int capacity = 10;
- private int usedSize;
-
- private T[] elem;
-
- //运用反射创建泛型数组
- public MyArrayList1(Class<T> clazz,int capacity) {
- elem = (T[]) Array.newInstance(clazz,capacity);
- }
- }
通配符是用来解决泛型无法协变的问题的,协变指的就是如果 Cat 是Animal 的子类,那么 List<Cat> 也应 该是 List<Animal> 的子类。但是经过泛型的擦除机制,最终两个都擦除到object,所以泛型是不支持这样的父子类关系的。
? 用于在泛型的使用,即为通配符。
<? extends 上界 ><? extends Animal > // 可以传入的实参类型是Animal或者Animal的子类
<? extends Animal> 是 <Animal>及<Animal子类>的父类
<?> 是 <? extends Animal>的父类
- //构成父子类关系
- class Animal {
-
- }
- class Dog extends Animal {
-
- }
- class Dubianquan extends Dog {
-
- }
②父类引用子类对象:
- public class Zoon {
-
- public static void main(String[] args) {
-
- //<? extends Animalr> 是 <Animal>及<Animal子类>的父类
- ArrayList<? extends Animal> arrayList1 = new ArrayList<Animal>();
- ArrayList<? extends Animal> arrayList2 = new ArrayList<Dog>();
- ArrayList<? extends Animal> arrayList3 = new ArrayList<Dubianquan>();
-
- //<?> 是 <? extends Animalr>的父类
- ArrayList<?> arrayList = arrayList1;
- }
- }
通配符的上界只适合于读取数据,不适用于写入数据。因为通配符上界引用的是<Animal及其子类>的对象,但是不能确定到底是哪个子类,不能存储确定的子类类型,java在编译时会自动查错。
- class Animal {
- public String toString() {
- return "Animal :>";
- }
- }
- class Cat extends Animal {
- @Override
- public String toString() {
- return "Cat :>";
- }
- }
- class Dog extends Animal {
- @Override
- public String toString() {
- return "Dog :>";
- }
- }
-
- public class Zoon {
-
- //通配符的上界是不适用于写入的,只适合于读取
- public static void main0(String[] args) {
-
- ArrayList<? extends Animal> arrayList = new ArrayList<>();
-
- //报错
- //arrayList.add(new Animal());
- //arrayList.add(new Dog());
-
- //但是可以读取数据,用泛型的上界来读取子类的数据,属于向上转型
- Animal animal = arrayList.get(0);
-
- //也可以用Object,因为他是所有类的父类
- Object o = arrayList.get(0);
- }
-
- }
<? super 下界 ><? super Animal > // 代表 可以传入的实参的类型是 Animal 或者 Animal 的父类类型
<? super Animal> 是 <Animal>及<Animal父类>的父类
<?> 是 <? super Animal>的父类
- //构成父子类关系
- class Animal {
-
- }
- class Dog extends Animal {
-
- }
- class Dubianquan extends Dog {
-
- }
- public class Zoon {
-
- public static void main(String[] args) {
-
- //<? super Dog> 下界是Dog,引用的是<Dog及其父类>的对象
- //因为<? super Dog>的上界没有限制,所以一直可以到Object。
- //因此<? super Dog>是<任一Dog及其父类>的父类
- //符合父类引用子类对象的规则
- ArrayList<? super Dog> arrayList1 = new ArrayList<Animal>();
- ArrayList<? super Dog> arrayList2 = new ArrayList<Dog>();
-
- //报错,下界是Dog,不能引用下界以下的子类
- // ArrayList<? super Dog> arrayList3 = new ArrayList<Dubianquan>();
-
- //<?> 是 <? super Dog> 的父类
- ArrayList<?> arrayList = arrayList1;
-
- }
- }
通配符的下界只适合于写入数据,不适用于读取数据。因为通配符下界引用的是<Dog及其父类>的对象,但是不能确定读取到的是哪个父类类型,所以引用的类型不能确定,也就不能读取,但是Object是所有类的父类,非要读取的话可以用Object来接受。因为下界存储的内容都是下界以下的数据类型,所以适合写入数据。
- class Animal {
- @Override
- public String toString() {
- return "Animal{}";
- }
- }
- class Dog extends Animal {
- @Override
- public String toString() {
- return "Dog{}";
- }
- }
- class Dubianquan extends Dog {
- @Override
- public String toString() {
- return "Dubianquan{}";
- }
- }
-
- public class Zoon {
-
- public static void main(String[] args) {
- ArrayList<? super Dog> arrayList1 = new ArrayList<Animal>();
-
-
- ArrayList<? super Dog> arrayList2 = new ArrayList<Dog>();
-
- //报错,存储的数据是Dog及其子类的数据类型
- // arrayList2.add(new Animal());
-
- //添加的元素 是Dog或者Dog的子类
- arrayList2.add(new Dog());
- arrayList2.add(new Dubianquan());
-
- //ArrayList<? super Dog> arrayList2引用的是Dog及其父类对象
- //编译器会考虑到ArrayList<? super Dog> arrayList2引用的对象如果是 new ArrayList<Animal>();
- //那么它存储的数据可能会有Animal类型的数据,那么就不能用Dog来接受Animal类型的数据。
- // Dog dog = arrayList2.get(0);//报错
-
- // Animal animal = arrayList2.get(0);//报错 原理同上
-
- //但是Object是所有类的父类,可以使用其来读取数据
- Object o = arrayList2.get(0);
- }
- }
- class Animal {
- @Override
- public String toString() {
- return "Animal{}";
- }
- }
- class Dog extends Animal {
- @Override
- public String toString() {
- return "Dog{}";
- }
- }
- class Dubianquan extends Dog {
- @Override
- public String toString() {
- return "Dubianquan{}";
- }
- }
-
- public class Zoon {
-
-
- public static void main(String[] args) {
- //通配符下界存储数据
- ArrayList<? super Dog> arrayList1 = new ArrayList<Dog>();
- arrayList1.add(new Dog());
- arrayList1.add(new Dubianquan());
-
- //要用通配符下界读取数据,就要用Object来接受
- for (Object O: arrayList1) {
- System.out.println(O);
- }
-
- //利用通配符上界来读取数据
- ArrayList<? extends Animal> arrayList = (ArrayList<? extends Animal>) arrayList1;
-
- //因为上述存储的数据类型都是Animal的子类,所以可以用Animal来接受
- for (Animal a: arrayList) {
- System.out.println(a);
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。