目录
一、泛型
二、集合中,较器中使用泛型
三、自定义泛型类和接口
四、自定义泛型方法
四、通配符 ?
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值或参数的类型。这个类型参数将在使用时(例如,继承或实现这个接口、创建对象或调用方法时)确定(即传入实际的类型参数,也称为类型实参)。
@Test
public void test1(){
ArrayList arrayList = new ArrayList<>();
for (int i = 1; i <= 4 ; i++) {
arrayList.add(i);
}
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
System.out.println(next);
}
}
@Test
public void test2(){
HashMap map = new HashMap<>();
map.put("杰瑞",3);
map.put("汤姆",3);
var entries = map.entrySet();
var iterator = entries.iterator();
while (iterator.hasNext()){
var next = iterator.next();
String key = next.getKey();
Integer value = next.getValue();
System.out.println(key + "\t" + value);
}
}
在集合中使用泛型解决存在的问题
①、数据类型不安全,add()的参数是Object类型,意味着任何类型的对象都可以添加成功。
②、需要使用强制转换,繁琐。还可能导致ClassCastExcetion异常。
使用说明
集合框架在声明接口和其实现类时,使用了泛型(JDK5.0),在实例化集合对象时,如果没有使用泛型,默认操作是Object类型的数据,如果使用了泛型,则需要指明泛型的具体类型。
public class Employee implements Comparable{
private String name;
private Integer age;
private MyDate birthday;
public Employee() {
}
public Employee(String name, Integer age, MyDate birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public MyDate getBirthday() {
return birthday;
}
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 employee) {
//按照年龄从低到高
return this.age.compareTo(employee.age);
}
}
public class 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 void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
@Override
public String toString() {
return year + "年" + month + "月" + day + "日";
}
}
@Test
public void test1(){
TreeSet set = new TreeSet<>();
for (int i = 1; i <= 5 ; i++) {
set.add(new Employee("杰瑞" + i,i,new MyDate(2022 + i,i,i)));
}
var iterator = set.iterator();
while (iterator.hasNext()){
Employee employee = iterator.next();
System.out.println(employee);
}
}
@Test
public void test2(){
Comparator comparator = new Comparator<>(){
@Override
public int compare(Employee o1, Employee o2) {
int year = o1.getBirthday().getYear() - o2.getBirthday().getYear();
if (year != 0){
return year;
}
int month = o1.getBirthday().getMonth() - o2.getBirthday().getMonth();
if (month != 0){
return month;
}
return o1.getBirthday().getDay() - o2.getBirthday().getDay();
}
};
TreeSet set = new TreeSet<>();
for (int i = 1; i <= 5 ; i++) {
set.add(new Employee("杰瑞" + i,i,new MyDate(2022 + i,i,i)));
}
var iterator = set.iterator();
while (iterator.hasNext()){
Employee employee = iterator.next();
System.out.println(employee);
}
}
public class Order {
T t;
String name;
public Order() {
}
public Order(T t, String name) {
this.t = t;
this.name = name;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Order{" +
"t=" + t +
", name='" + name + '\'' +
'}';
}
}
@Test
public void test1(){
//默认泛型是Object
Order
① 我们在声明完自定义泛型类以后,可以在类的内部(比如:属性、方法、构造器中)使用类的泛型。
② 我们在创建自定义泛型类的对象时,可以指明泛型参数类型。一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型。
③ 如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
- 经验:泛型要使用一路都用。要不用,一路都不要用。
④ 泛型的指定中必须使用引用数据类型。不能使用基本数据类型,此时只能使用包装类替换。
⑤ 除创建泛型类对象外,子类继承泛型类时、实现类实现泛型接口时,也可以确定泛型结构中的泛型参数。
如果我们在给泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用泛型参数。
我们还可以在现有的父类的泛型参数的基础上,新增泛型参数。
注意事项:
① 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:
② JDK7.0 开始,泛型的简化操作:ArrayList flist = new ArrayList<>();
③ 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
④ 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];
参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
⑤ 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,但不可以在静态方法中使用类的泛型。
⑥ 异常类不能是带泛型的。
public class MethodTest {
public E method(E e){
return e;
}
}
注意:
①、方法,也可以被泛型化,与其所在的类是否是泛型类没有关系。
②、泛型方法中的泛型参数在方法被调用时确定。
③、泛型方法可以根据需要,声明为static的
@Test
public void test1(){
List> list = null;
ArrayList arrayList = new ArrayList<>();
arrayList.add("杰瑞");
list = arrayList;
Object o = list.get(0);
//不允许添加数据
//list.add("汤姆");
}
读写数据特点:
读取数据:允许,读取的数据是Object类型
写入数据:不允许
- 通配符指定上限: extends 类/接口 >
- 使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
- 通配符指定下限: super 类/接口 >
- 使用时指定的类型必须是操作的类或接口,或者是操作的类的父类或接口的父接口,即>=