1. 什么是泛型
所谓泛型了就是允许在定义类、接口时通过一个“标识”表示类中某个“属性的类型”或者是某个方法的“返回值或参数的类型”。这个类型参数将在使用时(例如,继承或实现这个接口、创建对象或调用方法时)确定(即传入实标的类型参数,也称为类型实参)。
1.1 在集合中使用泛型之前可能存在的问题
问题1,类型不安全。因为add()的参数是0bject类型,意味着任何类型的对象都可以添加成功
问题2,需要使用强转操作,繁琐。还有可能导致CLassCastException异常。
1.2 在集合中使用泛型的例子
//在集合中使用泛型的例子
@Test
public void test01(){
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(14);
list.add(634);
list.add(73);
list.add(42);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
int score;
score = iterator.next();
System.out.println(score);
}
}
//在MAP中使用泛型的例子
@Test
public void test02(){
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Tom",52);
map.put("Luccy",75);
map.put("James",123);
map.put("Sam",77);
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entrySet.iterator();
while (iterator.hasNext()){
Map.Entry<String, Integer> entry = iterator.next();
String name = entry.getKey();
Integer value = entry.getValue();
System.out.println(name + "----" + value);
}
}
1.3 在比较器中使用泛型的例子
MyDate类
public class MyDate implements Comparable<MyDate>{
private int year;
private int month;
private int day;
public MyDate() {
}
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public int getMonth() {
return month;
}
public int getDay() {
return day;
}
public void setYear(int year) {
this.year = year;
}
public void setMonth(int month) {
this.month = month;
}
public void setDay(int day) {
this.day = day;
}
@Override
public String toString() {
return
year + " 年" + month +
" 月" + day + " 日";
}
@Override
public int compareTo(MyDate o) {
int yeardistince = this.getYear() - o.getYear();
if(yeardistince != 0){
return yeardistince;
}
int monthdistince = this.getMonth() - o.getMonth();
if(monthdistince != 0){
return monthdistince;
}
return this.getDay() - o.getYear();
}
}
Employee类
public class Employee implements Comparable<Employee>{
private String name;
private int age;
private MyDate birthday;
public Employee() {
}
public Employee(String name, int age, MyDate birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public MyDate getBirthday() {
return birthday;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + ''' +
", age=" + age +
", birthday=" + birthday +
'}';
}
@Override
public int compareTo(Employee o) {
if(o == this){
return 0;
}
return this.name.compareTo(o.name);
}
}
执行类
public class Employee_Test {
//自然排序 按名字进行排序 (从小到大)
@Test
public void test01(){
Employee employee1 = new Employee("Tom",18,new MyDate(2000,1,1));
Employee employee2 = new Employee("Luccy",20,new MyDate(1999,2,2));
Employee employee3 = new Employee("Kevin",30,new MyDate(1990,3,3));
Employee employee4 = new Employee("James",38,new MyDate(1985,5,5));
Employee employee5 = new Employee("Curry",32,new MyDate(1989,6,6));
//自然排序
TreeSet<Employee> treeSet = new TreeSet<Employee>();
treeSet.add(employee1);
treeSet.add(employee2);
treeSet.add(employee3);
treeSet.add(employee4);
treeSet.add(employee5);
Iterator<Employee> iterator = treeSet.iterator();
while(iterator.hasNext()){
Employee employee = iterator.next();
System.out.println(employee);
}
}
//定制排序 按生日大小排序(从小到大)
@Test
public void test02(){
Employee employee1 = new Employee("Tom",18,new MyDate(2000,1,1));
Employee employee2 = new Employee("Luccy",20,new MyDate(1999,2,2));
Employee employee3 = new Employee("Kevin",30,new MyDate(1990,3,3));
Employee employee4 = new Employee("James",38,new MyDate(1985,5,5));
Employee employee5 = new Employee("Curry",32,new MyDate(1989,6,6));
//写法1
/*
Comparator<Employee> comparator = new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
int yeardistince = o1.getBirthday().getYear() - o2.getBirthday().getYear();
if(yeardistince != 0){
return yeardistince;
}
int monthdistince = o1.getBirthday().getMonth() - o2.getBirthday().getMonth();
if(monthdistince != 0){
return monthdistince;
}
return o1.getBirthday().getDay() - o2.getBirthday().getYear();
}
@Override
public boolean equals(Object obj) {
return false;
}
};
*/
//写法2 再重写comparator接口时,在接口内部调用自己定义的MyDate的compare(MyDate的自然排序)
Comparator<Employee> comparator = new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
return o1.getBirthday().compareTo(o2.getBirthday());
}
};
TreeSet<Employee> treeSet = new TreeSet<Employee>(comparator);
treeSet.add(employee1);
treeSet.add(employee2);
treeSet.add(employee3);
treeSet.add(employee4);
treeSet.add(employee5);
Iterator<Employee> iterator = treeSet.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
1.4.使用说明
集合框架在声明接口和其实现类时,使用了泛型(jdk5.0),在实例化集合对象时如果没有使用泛型,则认为操作的是object类型的数据。
如果使用了泛型,则需要指明泛型的具体类型。一旦指明了泛型的具体类型,则在集合的相关的方法中,凡是使用类的泛型的位置,都替换为具体的泛型类型。
2. 泛型类
2.1 实例
GenericCustom 泛型类
public class GenericCustom<A,B,C> {
private A a;
private B b;
private C c;
public GenericCustom() {
}
public GenericCustom(A a, B b, C c) {
this.a = a;
this.b = b;
this.c = c;
}
/**
* 静态方法不能使用类的泛型符号
*
* @param a
*/
public A method1(A a) {
return a;
}
public A getA() {
return a;
}
public B getB() {
return b;
}
public C getC() {
return c;
}
}
GenericCustom 的继承子类 SubGenericCustom
//1. SubGenericCustom1
public class SubGenericCustom1 extends GenericCustom{
//SubGenericCustom1不是泛型类
}
//2. SubGenericCustom2
public class SubGenericCustom2 extends GenericCustom<String, Double, Long>{
//SubGenericCustom2 也不是泛型类
}
//3. SubGenericCustom3
public class SubGenericCustom3<A,B,C> extends GenericCustom<A,B,C>{
//SubGenericCustom3 是泛型类
}
//4. SubGenericCustom4
public class SubGenericCustom4<A,B,C,D> extends GenericCustom<A,B,C>{
//此时SubGenericCustom4也是泛型类
D d;
public SubGenericCustom4() {
}
public SubGenericCustom4(D d,A a, B b, C c) {
super(a, b, c);
this.d = d;
}
public D GetD(){
return d;
}
public D show(D d){
return d;
}
}
在实例化时,可以指明类的泛型参数的具体类型
@Test
public void test03(){
GenericCustom<String, Double, Long> genericCustom = new GenericCustom<String, Double, Long>();
//泛型符号 如果不指定,统统当成Object来看
GenericCustom genericCustom2 = new GenericCustom();
//泛型参数在指明时,是不可以使用基本数据类型的!但是可以使用包装类替代基本数据类型。
GenericCustom<Integer,Integer,Integer> genericCustom3 = new GenericCustom<Integer,Integer,Integer>();
}
测试GenericCustom的子类
@Test
public void test04(){
//1. 实例化SubGenericCustom1,虽然是GenericCustom的子类,但SubGenericCustom不是泛型类
SubGenericCustom1 subGenericCustom1 = new SubGenericCustom1();
//子类SubGenericCustom1未指定GenericCustom<A,B,C>泛型,此A,B,C是Object
Object a = subGenericCustom1.getA();
//2. 实例化SubGenericCustom2,虽然是GenericCustom的子类,但SubGenericCustom不是泛型类
//因为指明了它的类型
SubGenericCustom2 subGenericCustom2 = new SubGenericCustom2();
//子类SubGenericCustom2指定GenericCustom<A,B,C>,A,B,C是String, Double, Long
String a1 = subGenericCustom2.getA();
//3. 实例化SubGenericCustom3,SubGenericCustom3是泛型类,继承了GenericCustom的未指明类型
SubGenericCustom3<String, Double, Long> subGenericCustom3 = new SubGenericCustom3<String, Double, Long>();
String submethod = subGenericCustom3.method1("hello SubGenericCustom3");
System.out.println(submethod);
//4. 实例化SubGenericCustom4,SubGenericCustom4是泛型类,比GenericCustom3多一个泛型对象
SubGenericCustom4<String,String,Double,Long> subGenericCustom4 = new SubGenericCustom4<String, String, Double, Long>();
Long show1 = subGenericCustom4.show(15042L);
System.out.println(show1);
}
2.2 使用说明
①我们在声明完自定义泛型类以后,可以在类的内部(比如,属性、方法、构造器中)使用类的泛型。
②我们在创建自定义泛型类的对象时,可以指明泛型参数类型。一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型。
③如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照bject处理,但不等价于object。
经验:泛型要使用一路都用。要不用,一路都不要用。
④泛型的指定中必须使用引用数据类型。不能使用基本数据类型,此时只能使用包装类替换。
⑤除创建泛型类对象外,子类继承泛型类时、实现类实现泛型接口时,也可以确定泛型结构中的泛型参数。
---如果我们在给泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用泛型参数。
---我们还可以在现有的父类的泛型参数的基础上,新增泛型参数。
MARK
①不能使用new E[ ]。但是可以,E[ ] elements = (E[ ])new Object[capacity];
②在类/接口上声明的泛型,在本类或本接口中即代表某种类型,但不可以在静态方法中使用类的泛型。
3. 泛型方法
3.1 格式
权限修饰符 <T> 返回值类型 方法名(形参列表) //通常在形参列表或返回值类型的位置会出现泛型参数T
3.2 实例
//定义泛型方法,将E[]数组元素添加到对应类型的ArrayList中,并返回
public <E> ArrayList<E> copyArrayToList(E[] arr){
ArrayList<E> list = new ArrayList<E>();
for (E e:arr) {
list.add(e);
}
return list;
}
//测试定义泛型方法
@Test
public void test05(){
GenericCustom genericCustom = new GenericCustom();//此时定义对象的泛型跟泛型方法的泛型不是一个
Integer[] arr = new Integer[]{1,2,4,56};
System.out.println(genericCustom.copyArrayToList(arr));
}
3.3 MARK
声明泛型方法时,一定要添加泛型参数<T>
泛型参数在方法调用时,指明其具体的类型
泛型方法可以根据需要声明为statics
泛型方法所属的类是否是一个泛型类,都可以。