泛型
https://www.cnblogs.com/jinjiezhilu/p/6856742.html
泛型
1--jdk1.5出现的安全新特性(安全机制) 解决问题的
integer的自动装箱拆箱是解决简化问题
2--好处
1- 将运行时期的问题ClassCastException转到了编译时期(没有安全隐患了,程序员可以解决,前期控制住,后期很安全)
2- 避免了强制转化的麻烦
3--<>什么时候用?
1- 当操作引用数据类型不确定的时候,就是用<>。将要操作的应用数据类型传入即可。
2- 其实<>就是一个接受具体引用数据类型的参数范围。
void show(int x)
class ArrayList<E> 接受范围,即参数范围 E就是一个参数,因为接受引用数据类型,不是类就是接口,数组,因为首字母大写
因为ArrayList可以传任意类型,哥们不知道你要传什么,所以给你提供了类型变量
你想用什么,往里面传就可以。你传什么,我就给你操作什么,你定义什么,就只能操作这个类型。
没有这个之前是传入的Object,想用的时候就要强制转换
void show(int x,int y)
class Tool<e1,e2,e3> e不定义类型,是因为e就是来接受类型的 Tool<String,Integer,Person> t = new Tool<String,Integer,Person>;
3- 在程序中只要用到了带有<>的类或接口,就要明确传入的具体引用数据类型。
4--原理:
泛型提高了编译的安全性,就因为我编译的时候检查完了,运行的时候才没有事,使用在编译时期的安全技术,给编译器使用的,即泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全。
运行时,会将泛型去掉,生成的class文件中不带泛型,这个称之为泛型的擦除。
因为编译完生成class文件不带泛型的,为什么擦掉呢?运行时靠虚拟机去启动类型加载器,专门读取类文件,解析类文件并把类文件加载进内存。
我们只是编译时期将类型检查,检查是安全的没有的,运行就会把泛型去掉。因为此时类型已经同一了,我们还是希望可以用以前的类加载器进内存,因为以前加载器没有见过泛型。
为什么擦除?因为为了兼容运行的类加载器。如果类文件加入泛型,就意味5.0的类加载器要升级。
取出的时候不加强转,知道取的是什么东西?编译器只做类型检查,最终类型擦除,最终里面存的还是Object,不做强转,取出来还能用。但是类型不是已经擦出了吗?
为了避免强转这些问题,做了另外一个动作补偿。
哥们编译完以后完全不告诉类装载器不合适,我就搞了泛型补偿机制,告诉类装载器已经检测完类型,编写了补偿程序,在已用的类装载器只上编写补偿程序。
以前类装载器不动,就编写了一个补偿程序。
什么是补偿,来了以后根据你指定好的元素类型,真正运行要操作元素,元素存在了,根据你的元素获取其类型,再对你进行转换。
为什么能拿元素类型呢?
因为任何一个对象都有getClass()字节码文件,返回Clazz clazz.getName() 对其进行判断并进行转换
万一元素不一样呢? 编译器一旦检查完毕没有问题,类型就是统一的,不会出现元素不一致的情况。
泛型的补偿:在运行时通过获取元素类型进行转换动作,不用使用者再强制转换。
<E> <T>参数变量名,type element,变量名自己取的,不用管
泛型在集合框架中运行的最多,因为容器不能明确装什么类型对象,就用泛型表示,你指定什么类型就往里面存什么类型取什么类型。
引用类型不确定的时候采用泛型,基本数据类型不行。
不仅是给集合使用传参数就ok,还能为我们提供很多设计上的便捷。
package com.itcast.p1.generic.demo;
import java.util.ArrayList;
import java.util.Iterator;
public class GennericDemo {
public static void main(String[] args){
// ArrayList list = new ArrayList();
/**
* 泛型:对操作的类型进行指定
*/
ArrayList<String> list = new ArrayList<String>();
list.add("abc");//public boolean add (Object obj)
list.add("hahah");
// list.add(5);//list.add(new Integer(4)) java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String 编译的时候会检验类型,强类型语言,不带运行这一步,编译时期就已经挂了,安全一点,这时候绝对不能存。这时候就不需要强转,取出来就能用
Iterator<String> it = list.iterator();//迭代器从集合获取出来的,集合是String,迭代器取出来的也是String,后面也不需要墙砖了
while (it.hasNext()) {
/**
* 为什么需要强转?add定义参数类型是Object类型,就因为Object类型,什么类型对象都能接受进来,而且被提升为了Object,取的也肯定是Object
* 想去使用元素特用的方法,需要强转,一强转就出现了问题
* 如何解决这个问题?如果list存很多类型,挨个去判断嘛?判断麻烦而且判断不过来。。万一集合,别人传进来的,别人传什么我不知,如何判断,做不了
* 之前由程序员主管控制。如果存请存指定类型,传其他的会有隐患,编译没事,运行出事。用户出事,用户解决不了
* 后期提供了解决方案:
* 跟数组思想一致 int[] arr = new int[3];//声明时就知道里面元素 arr[0]=9;arr[0]=9.0;//
* 定义容器的时候明确里面装什么类型的元素 定义集合的时候明确存入该集合什么类型的元素
*/
String next = it.next();
System.out.println(next);
}
}
}
package com.itcast.p1.generic.demo;
import java.util.Iterator;
import java.util.TreeSet;
public class GennericDemo2 {
public static void main(String[] args) {
/**
* 使用容器的时候你就要明确容器里装什么元素
* person不具备比较性
*/
TreeSet<Person> ts = new TreeSet<Person>(new ComparetorByName());
ts.add(new Person("lisi1", 20));
ts.add(new Person("lisi2", 21));
ts.add(new Person("lisi3", 22));
ts.add(new Person("lisi4", 23));
ts.add(new Person("lisi5", 24));
ts.add(new Person("lisi6", 24));
ts.add(new Person("lisi7", 20));
Iterator<Person> it = ts.iterator();
while (it.hasNext()) {
Person p = it.next();
System.out.println(p.getAge()+" "+p.getName());
}
}
}
package com.itcast.p1.generic.demo;
/**
* 泛型自定义演示defineClass
*
*/
public class GennericDemo3 {
public static void main(String[] args){
Tool<Student> t = new Tool<Student> ();
t.setO(new Student());
// t.setO(new Worker());//刚才运行的时候才发现运行异常,编译的时候没问题;现在编译的时候就提示异常了,这就是泛型的优势,将运行时期的异常转换到了编译时期,同时避免了强转的麻烦,比Object安全,但是稍微麻烦点(替换方式)
Student o = t.getO();
// Tool t = new Tool();
//// t.setO(new Student());
// t.setO(new Worker());//存错了 编译ok,存的类型对,因为是Object,什么对象都可以运行
// // java.lang.ClassCastException: com.itcast.p1.generic.demo.Worker cannot be cast to com.itcast.p1.generic.demo.Student
// /**
// * 为了提高程序的扩展性,我们用的都是Object ,什么对象都能往里面进,因为出来什么不知道,但是往里面存操作对象,用Object接受
// * 提高了扩展性,但是有弊端,必须强转,不然不能使用
// */
// Student s = (Student) t.getO();
}
}
package com.itcast.p1.generic.demo;
/**
* 自定义方法
*/
public class GennericDemo4 {
public static void main(String[] args) {
Tool<String> t = new Tool<String>();
// t.show("abc");//跟着类走的
t.show("abc");//跟着类走的,一旦你使用了泛型,变量类型不确定,怎么使用具体一个类方法,但是无论你传什么对象父类都是Object,具备Object方法
t.print(new Integer(4));//跟着方法走的,自定义的
t.method("a"+"dbdd".length());
/**
* 挂了,因为我定义类是泛型,tool引用指向对象明确类型String,所以这个对象只能操作凡是String对象的方法,类型检查没过去,
* 我爱show什么展示什么,show方法操作类型不确定,Object可以,因为最终都是调用Object类中的方法,多态
* 如果不明确类型的情况下,Object其实是一种具体类型,是所有类的父类,导致可以多态方来接受
* 不明确类型,人家往里面传什么,你就操作什么,可以定义泛型public <W> void show(w str)前面定义参数,后面使用参数,先声明后使用
* 将泛型定义在方法上:这里就可以什么都可以show了,自动找类型匹配
*/
}
}
package com.itcast.p1.generic.demo;
public class Tool<O> {
/**
* 操作那个对象不知道,我操作什么对象靠你给我传递,定义泛型
* 在jdk1.5后,使用泛型来接受类中要操作的应用数据类型
* 泛型类:
* 什么适合用?
* 当类中的操作的引用数据类型不确定的时候,就使用泛型来表示
*
* */
private O o;
public O getO() {
return o;
}
public void setO(O o) {
this.o = o;
}
/**
* 不仅想显示Integer,还想显示String
* @param str
*/
// public void show(String str) {
// System.out.println("show "+str);
// }
// public void show(Object str) {
// System.out.println("show "+str);
// }
public void show(O str) {
System.out.println("show "+str);
}
/**
* 如果定义Object只能用Object
* 如果定义泛型机制还可以进行其他应用
* @param <Y>
* @param str
*/
public <Y>void show1(Y str) {
// 跟着类走的,一旦你使用了泛型,变量类型不确定,怎么使用具体一个类方法,但是无论你传什么对象父类都是Object,具备Object方法
// System.out.println("show "+str.length);
}
public void print(String str) {
System.out.println("show "+str);
}
/**
* 静态方法挂了
* 静态不需要对象的,泛型需要对象来明确,不知道泛型O怎么使用
* 又想需要什么类型都可以操作
* 当方法静态时,不能访问类上定义的泛型,如果静态方法使用泛型,只能讲泛型定义在方法上。
* 泛型放在返回值前面,修饰符后面
* @param str
* @return
*/
public static <Y> void method (Y str) {
System.out.println("show "+str);
}
//
// public void print(O str) {
// System.out.println("show "+str);
// }
//
public <W> void print(W str) {
System.out.println("show "+str);
}
}
/**
* 对Person对象的操作获取
* @author c_wangmengling-001
* 不指定泛型,每个类都需要对应的工具类,很麻烦,定义一个工具类操作所有对象
* 找到所有对象的共性,要操作猫操作狗,操作共性内容,动物
* 写了Object意味着什么对象都可以操作
*/
/**
public class Tool {
private Object o;
// private Person person;
//
// public Person getPerson() {
// return person;
// }
//
// public void setPerson(Person person) {
// this.person = person;
// }
public Object getO() {
return o;
}
public void setO(Object o) {
this.o = o;
}
}
*/
package com.itcast.p1.generic.demo;
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person() {
super();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", getAge()=" + getAge() + ", hashCode()=" + hashCode()
+ ", getName()=" + getName() + ", getClass()=" + getClass() + ", toString()=" + super.toString() + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
/**
* 因为继承父类,不能用泛型,父类没有定义泛型
* 必须先要判断类型强转
* if(!(obj.instanceof Person)){throw new RuntimeException}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Person p) {
int temp = this.age-p.age;
return temp==0 ? this.name.compareTo(p.name):temp;
}
}
package com.itcast.p1.generic.demo;
public class Student extends Person {
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(String name, int age) {
super(name, age);
// TODO Auto-generated constructor stub
}
}
package com.itcast.p1.generic.demo;
public class Worker extends Person {
public Worker() {
super();
// TODO Auto-generated constructor stub
}
public Worker(String name, int age) {
super(name, age);
// TODO Auto-generated constructor stub
}
}
package com.itcast.p1.generic.demo;
import java.util.Comparator;
public class ComparetorByName implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
int temp = o1.getName().compareTo(o2.getName());
return temp==0 ?o1.getAge()-o2.getAge():temp;
}
}
package com.itcast.p1.generic.demo;
public class GennericAdavanceDemo {
/**
* 如果多个集合,即集合不唯一,想要迭代另外一个。再写个迭代器不合适了,容器不断在变化,元素不断再添加,可是迭代都一样。为了提高方法的复用性,提取方法迭代集合。打印collection。搞一方法,无论哪个集合,都用这个方法来迭代,迭代并打印集合中的元素。但是如果是HashMap就不好使用。无论是谁collection ,扩展性高多了。不管你是什么,都能将你打印。如果里面放Integer,存的就是integer,这俩方法都不能同时存在,因为操作的都是集合,以为集合中存放的元素不确定,这个时候,当一旦操作的类型不确定的时候,里面还不准备用具体类型的情况下,就可以使用通配符来表示。泛型的通配符:?,即位置类型。这样写就废了。写了?,说明什么都可以传,但也可以什么都不用传,因为老版本没有泛型,兼容老版本,但是最好加上。其实这个?和一会写的差不多。未知类型就是当我们不明确类型情况下,我们可以用?来表示,。和这种方式区别 <T> (T t,)咋看没什么区别,但是有区别。当你在运行的时候,只要有一个类型具备了,都是会统一类型。区别并不大。如果你有指定T,T就意味可以代表一个具体类型并可以被操作,T可以被接受,有些工具返回类型为t的时候,后续是可以进行操作的。而对于?只仅在不明确类型并不对这种类型操作来表示。如果仅仅打印,不用谢写这么复杂代码,直接打印即可,
*/
}
package com.itcast.p1.generic.demo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class GennericAdavanceDemo2 {
public static void main(String[] args) {
ArrayList<Worker> a1 = new ArrayList<Worker>();
a1.add(new Worker("abc",30));
a1.add(new Worker("abc4",34));
/**
* 这里面存的除了worker,就是student
*/
ArrayList<Student> a2 = new ArrayList<Student>();
a2.add(new Student("abc",40));
a2.add(new Student("abc4",44));
/**
* 以前玩Object什么都可以接受来,现在做限定以后,只能有一部分类型进来,如果装的不是一个类型,或是不是其子类,直接编译失败,不让他进来,将运行时期问题向编译时期转移
*/
ArrayList<Integer> a23 = new ArrayList<Integer>();
a23.add(40);
a23.add(44);
//printCollection(a23);这样就不行,称为泛型的限定,只接受Person的子类
// printCollection(a1);
printCollection(a2);
}
/**
* 迭代并打印容器
* 现在有新需求,只打印Person的子类,只想操作一部分,只是想操作自己定义的一套体系
* @param al
* Collection<Animal> al = new ArraryList<Dog>();声明我有集合可以存动物,针对容器实体只能存狗,可以我猫不就出事了嘛
* Collection<Dog> al = new ArraryList<Animal>();声明里面存狗,但实体里面什么都可以存,那还不是出事嘛
* 一般情况下,左右类型要一致,但是也有特殊性情况
* (Collection<Person> al) 这要操作不可取
* (Collection<? extends Person> al) 即可
* 其实? 代表的? extends Object
* 可以对类型进行限定:
* ? extends E :接受E类型或E的子类对象。上限!
* ? super E :接受E类型和E的父类型。下限! 举例不太明显
*/
/*private static void printCollection(Collection<?> al) {
// TODO Auto-generated method stub
Iterator<?> it = al.iterator();
while (it.hasNext()) {
System.out.println(it.next().toString());
}
}*/
/*private static void printCollection(Collection<? extends Person> al) {
// TODO Auto-generated method stub
Iterator<? extends Person> it = al.iterator();
// Iterator<?> it = al.iterator();
while (it.hasNext()) {
// System.out.println(it.next().toString());
Person p = it.next();
System.out.println(p.getAge()+" "+p.getName());
}
}*/
/**
* 迭代器的泛型百分之百取迭代器集合的泛型一致
* 应用不明显
*/
private static void printCollection(Collection<? super Student> al) {
Iterator<? super Student> it = al.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
package com.itcast.p1.generic.demo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/**
* 泛型的应用
* 什么时候使用?
* 一般在存储元素的时候都使用上限,因为这样取出都是按照上限类型来运算,不会出现类型安全隐患 --上限用的多
* TreeSet(Comparator<? super E> comparator) --需要会用
* 通常对集合中的元素进行取出操作时,可以使用下限。--你存什么类型,我以取出来用父类型来接受,保证我都能接收到
*
*
*
*/
public class GennericAdavanceDemo3 {
public static void main(String[] args) {
TreeSet<Worker> a1 = new TreeSet<Worker>();
// TreeSet a1 = new TreeSet();
a1.add(new Worker("abc1",30));
a1.add(new Worker("abc41",34));
TreeSet<Student> a2 = new TreeSet<Student>(new CompByStuName());
a2.add(new Student("abc2",40));
a2.add(new Student("abc42",44));
TreeSet<Person> a23 = new TreeSet<Person>(new ComparetorByName());
/**
* 添加一个元素
*/
a23.add(new Person("abc3",50));
a23.add(new Person("abc43",54));
/**
* 现在想添加一堆元素
*/
TreeSet<String> a24 = new TreeSet<String>();
// a24.addAll(a1);//不可以添加,没有泛型限定的时候可以添加,这样存的时候没有问题,取的时候有问题,类型会有安全隐患。这时候就加上了泛型的限定,错误类型不匹配
/**
* 后期产生了子类对象,想传子类对象是否可以呢
* 所以可以设计方法获取E类型和E的子类型,不存在安全隐患,扩展性很强,不光E可以,子类
* 什么时候使用?通常存元素,用上限。因为一旦确定好元素以后,你往里面添加这个类型和子类型都可以,因为同一按照E类型来取的]
* TreeSet(Comparator<? super E> comparator)
* 如果学生比,是不是要创建一个学生比较器
* 无论你比的人还是学生,你们都是先比较姓名再比年龄
* 学生用的是这个,工人用的也是这个,比较器都用的一个。用的都是父类型的东西
* 如果要想对这里学生工人统一进行共性类型name和age父类型的排序需要怎么进行?
* 如果你指定的什么类型,用的都是人的东西,所以用本类和父类都一样
* 把集合中元素要取出来比较,所以要把取出来的元素进行接收,接受的时候你存放的是什么类型我不知道,我接受的类型要大一些
*/
// a24.add("ssss");
a23.addAll(a1);
// System.out.println(a23.size());
/**
* 我想按照姓名排序
* 自定义比较器
*/
// Iterator<Person> it = a23.iterator();
Iterator<Student> it = a2.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// ArrayList<Worker> a1 = new ArrayList<Worker>();
//// ArrayList a1 = new ArrayList();
// a1.add(new Worker("abc1",30));
// a1.add(new Worker("abc41",34));
//
// ArrayList<Student> a2 = new ArrayList<Student>();
// a2.add(new Student("abc2",40));
// a2.add(new Student("abc42",44));
//
// ArrayList<Person> a23 = new ArrayList<Person>();
// /**
// * 添加一个元素
// */
// a23.add(new Person("abc3",50));
// a23.add(new Person("abc43",54));
// /**
// * 现在想添加一堆元素
// */
// ArrayList<String> a24 = new ArrayList<String>();
//// a24.addAll(a1);//不可以添加,没有泛型限定的时候可以添加,这样存的时候没有问题,取的时候有问题,类型会有安全隐患。这时候就加上了泛型的限定,错误类型不匹配
// /**
// * 后期产生了子类对象,想传子类对象是否可以呢
// * 所以可以设计方法获取E类型和E的子类型,不存在安全隐患,扩展性很强,不光E可以,子类
// * 什么时候使用?通常存元素,用上限。因为一旦确定好元素以后,你往里面添加这个类型和子类型都可以,因为同一按照E类型来取的
// */
//// a24.add("ssss");
// a23.addAll(a1);
//
// System.out.println(a23.size());
}
class CompByName implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
int temp = o1.getName().compareTo(o2.getName());
return temp==0?o1.getAge()-o2.getAge():temp;
}}
}
/**
* 如果我们要定义集合
* 明确的什么类型,添加的就是什么类型
*/
class MyCollectin<E>{
/**
* 一次添加一个元素比较慢
* @param e
*/
public void add(E e) {}
/**
* 你里面装什么类型,添加新集合的也就是什么类型
* @param e
*/
// public void addAll(MyCollectin<E> e) {}
public void addAll(MyCollectin<? extends E> e) {}
}
class CompByStuName implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
int temp = o1.getName().compareTo(o2.getName());
return temp==0?o1.getAge()-o2.getAge():temp;
}
}
package com.itcast.p1.generic.demo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/**
* 泛型的应用
* 什么时候使用?
* 一般在存储元素的时候都使用上限,因为这样取出都是按照上限类型来运算,不会出现类型安全隐患 --上限用的多
* TreeSet(Comparator<? super E> comparator) --需要会用
* 通常对集合中的元素进行取出操作时,可以使用下限。--你存什么类型,我以取出来用父类型来接受,保证我都能接收到
* ?什么时候使用 没有上下限
*
*
*
*
*/
public class GennericAdavanceDemo4 {
public static void main(String[] args) {
ArrayList<Worker> a1 = new ArrayList<Worker>();
a1.add(new Worker("abc",30));
a1.add(new Worker("abc4",34));
/**
* 这里面存的除了worker,就是student
*/
ArrayList<Student> a2 = new ArrayList<Student>();
a2.add(new Student("abc",40));
a2.add(new Student("abc4",44));
ArrayList<Person> a3 = new ArrayList<Person>();
a3.add(new Person("abc",40));
a3.add(new Person("abc4",44));
ArrayList<Integer> a4 = new ArrayList<Integer>();
a4.add(40);
a4.add(44);
/**
* 包含的原理是用equals做判断
* equals是不是任何对象都具备,equlas中参数是不是Object
* 传入的时候什么都行,所以?就行。
* 一般只要用的全是Object方法就用
*/
boolean contains = a4.contains(a2);
}
private static void printCollection(Collection<?> al) {
// TODO Auto-generated method stub
Iterator<?> it = al.iterator();
while (it.hasNext()) {
System.out.println(it.next().toString());
}
}
/**
* containsAll(Collection<?> c)
*/
}