Java中泛型在集合中的应用
- 1.泛型的好处和应用规范:
- 2.方法上定义泛型:
- 3.在类上定义泛型:
- 4.在接口上定义泛型:
- 5.泛型的上下限:
泛型是JDK1.5新特性
1.泛型的好处和应用规范:
- 将运行时的异常提前至了编译时
- .避免了没必要的强制类型转换
- 泛型在集合中常见应用和规范
ArrayList<String> alist = new ArrayList<String>();正确写法 推荐使用
ArrayList<Object> alist = new ArrayList<String>();错误写法
ArrayList<String> alist = new ArrayList<Object>();错误写法
ArrayList alist = new ArrayList<String>();正确写法(为了兼容jdk1.5之前的代码)
ArrayList<String> alist = new ArrayList();正确写法(为了兼容jdk1.5之前的代码)
- 泛型没有多态的概念,左右两边的数据类型必须一致,或者只是写一边的泛型类型
举例:
package com.test;
import java.util.ArrayList;
public class Test1 {
public static void main(String[] args) {
//没使用泛型之前
ArrayList alist = new ArrayList();
alist.add("hello ");
alist.add("baby ");
alist.add(521);
System.out.println(alist);
//想要把数组里面的数据全部大写输出
for (int i = 0; i < alist.size(); i++) {
String str=(String) alist.get(i);
System.out.println(str.toUpperCase()); //toUpperCase()是String特有的方法 所以会出现java.lang.ClassCastException异常
}
//使用泛型
ArrayList<String> alist2 = new ArrayList<String>();
alist2.add("hello ");
alist2.add("baby ");
//alist2.add(521); //使用泛型后,添加的数据必须是String,如果不是在编译时报错
alist2.add("521");
System.out.println(alist2);
//想要把数组里面的数据全部大写输出
for (int i = 0; i < alist2.size(); i++) {
String str=alist2.get(i); //这里也不需要进行强制转换
System.out.println(str.toUpperCase());
}
}
}
2.方法上定义泛型:
- 方法上定义泛型的格式
修饰符 <声明定义泛型>返回值类型 函数名(使用定义的泛型){
} - 在泛型中不能使用基本数据类型,如果需要使用基本数据类型,那么就使用基本数据类型对应的包装类型
基本数据类型和对应的包装类型
byte Byte
short Short
int Integer
long Long
double Double
float Float
Boolean Boolean
char Character - 方法上定义泛型注意的事项:
1.在方法上自定义泛型,这个自定义泛型的具体数据类型是在调用方法的时候传入实参时确定具体数据类型的
2.自定义泛型只要符合标识符的命名规则即可。但是自定义泛型我们一般都习惯使用一个大写字母表示,如T,E。
举例1:
package com.test;
public class Test2 {
public static void main(String[] args) {
String str = getData("abc");
int i = getData(521);
System.out.println(i);
}
public static <T> T getData(T t) {
return t;
}
}
举例2: 需求:编写一个数组的工具类
package com.test;
class MyArrays{
//元素反转方法
public <T>void reverse(T[] arr){
for (int start = 0,end=arr.length-1; start < end; start++,end--) {
T temp=arr[start];
arr[start]=arr[end];
arr[end]=temp;
}
}
//toString方法
public <T>String toString(T[] arr) {
StringBuilder sb=new StringBuilder();
for(int i=0;i<arr.length;i++) {
if(i==0) {
sb.append("["+arr[i]+",");
}else if(i==arr.length-1){
sb.append(arr[i]+"]");
}else {
sb.append(arr[i]+",");
}
}
return sb.toString();
}
}
public class Test3 {
public static void main(String[] args) {
Integer [] arr= {1,2,3,4,5,6,7,8,9};//泛型种不能使用基本数据类型,所以这里用的是Integer,不能用int
String [] str= {"abc","def","ghi","jkl"};
MyArrays array=new MyArrays();
array.reverse(arr);
array.reverse(str);
System.out.println(array.toString(arr)); //[9,8,7,6,5,4,3,2,1]
System.out.println(array.toString(str)); //[jkl,ghi,def,abc]
}
}
3.在类上定义泛型:
- 泛型类的定义格式:
class 类名<声明定义的泛型>{
} - 泛型类要注意的事项:
1.在类上自定义泛型的具体数据类型是在使用该类的时候创建对象时候确定的
2.如果一个类在类上声明了自定义泛型,使用该类创建对象的时候没有指定泛型的具体数据类型,那么默认为Object类型
3.在类上自定义泛型不能作用于静态的方法,如果静态的方法需要使用自定义泛型,那么需要在方法上自己声明使用。
举例:
package com.test;
class MyArrays2<T>{
//元素反转
public void reverse(T[] arr){
for (int start = 0,end=arr.length-1; start < end; start++,end--) {
T temp=arr[start];
arr[start]=arr[end];
arr[end]=temp;
}
}
public String toString(T[] arr) {
StringBuilder sb=new StringBuilder();
for(int i=0;i<arr.length;i++) {
if(i==0) {
sb.append("["+arr[i]+",");
}else if(i==arr.length-1){
sb.append(arr[i]+"]");
}else {
sb.append(arr[i]+",");
}
}
return sb.toString();
}
public static <T2>void print(T2[] t){
//在类上自定义泛型不能作用于静态的方法,如果静态的方法需要使用自定义泛型,那么需要在方法上自己声明使用.
}
}
public class Test4 {
public static void main(String[] args) {
Integer [] arr= {1,2,3,4,5,6,7,8,9};//泛型种不能使用基本数据类型
String [] str= {"abc","def","ghi","jkl"};
MyArrays2<Integer> array1=new MyArrays2<Integer>();//创建对象时确定数据的具体类型为Integer
array1.reverse(arr);
System.out.println(array1.toString(arr));
MyArrays2<String> array2=new MyArrays2<String>();//创建对象时确定数据的具体类型为String
array2.reverse(str);
System.out.println(array2.toString(str));
MyArrays2 array3=new MyArrays2();//如果创建对象时没有指定泛型数据类型,默认为Object
array3.reverse(arr);
array3.reverse(str);
System.out.println(array3.toString(arr)); // [1,2,3,4,5,6,7,8,9]//上面反转之后,又反转回来
System.out.println(array3.toString(str)); // [abc,def,ghi,jkl]
}
}
4.在接口上定义泛型:
- 泛型接口的定义格式:
interface 接口名<声明自定义泛型>{
} - 泛型接口要注意的事项:
1.接口上自定义的泛型的具体数据类型是在实现一个接口的时候指定的。
2.在接口上自定义的泛型如果在实现接口的时候没有指定具体的数据类型,那么默认为Object类型
3.如果目前实现一个接口的时候,还不明确目前要操作的数据类型,要等待创建接口实现类对象的时候才能指定泛型的具体数据类型,可以在接口实现类上也使用泛型。
举例:
package com.test;
interface Dao<T>{
public void add(T t);
}
// 在接口使用泛型确定数据类型
class Demo1 implements Dao<String> {
@Override
public void add(String t) {
}
}
// 在接口实现类上也使用泛型
class Demo2<T> implements Dao<T>{
@Override
public void add(T t) {
}
}
public class Test5{
public static void main(String[] args) {
Demo1 demo1=new Demo1();
demo1.add("abc");
Demo2<String> demo2=new Demo2<String>();
demo2.add("abc");
Demo2<Integer> demo3=new Demo2<Integer>();
demo3.add(123);
}
}
5.泛型的上下限:
泛型中的通配符:? 可以匹配任何数据类型 一般不单独使用
- 需求1:定义一个函数可以接受任意类型的集合对象,要求接受的集合对象只能存储Dem2或者Dem2的父类类型数据
? super Dem2 :只能存储Dem2或者是Dem2父类元素。称为泛型的下限。 - 需求2:定义一个函数可以接受任意类型的集合对象,要求接受的集合对象只能存储Dem2或者Dem2的子类类型数据
? extends Dem2 :只能存储Dem2或者Dem2的子类对象。称为泛型的上限。
举例:
package com.test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
class Dem1{
}
class Dem2 extends Dem1{
}
class Dem3 extends Dem2{
}
public class Test6 {
public static void main(String[] args) {
ArrayList<Dem1> list1=new ArrayList<Dem1>();
ArrayList<Dem2> list2=new ArrayList<Dem2>();
ArrayList<Dem3> list3=new ArrayList<Dem3>();
HashSet<Dem2> set1 = new HashSet<Dem2>();
//需求1:泛型的下限
print(list1);//Dem1类型的ArrayList集合可以
print(list2);//Dem2类型的ArrayList集合可以
//print(list3);//Demo3类型的ArrayList集合不可以 //泛型的下限指定了只能是Dem2类型或者是Dem2类型的父类
print(set1);//Dem2类型的HashSet集合可以
//需求2:泛型的上限
//getData(list1);//Dem1类型的ArrayList集合可以 //泛型的上限指定了只能是Dem2类型或者是Dem2类型的子类
getData(list2);//Dem2类型的ArrayList集合可以
getData(list3);//Dem3类型的ArrayList集合可以
getData(set1);//Dem2类型的HashSet集合可以
}
//泛型的下限
public static void print(Collection<? super Dem2> c) {
}
//泛型的上限
public static void getData(Collection<? extends Dem2> c) {
}
}