首页
复制
搜索
前进
后退
重载网页
和我当邻居
给我留言吧
首页
壁纸
影院
统计
关于
友链
留言
Search
1
面向对象核心
28 阅读
2
Java枚举类
26 阅读
3
Lambda表达式与Stream
23 阅读
4
Java常用API与类
20 阅读
5
集合框架主要集合类及其用法
20 阅读
随笔
JAVA
Mysql
JavaWeb
Tools
登录
Search
标签搜索
集合框架
fnm
枚举
数组
基础语法
面向对象
Comparator
Comparable
Cloneable
内部类
普通成员内部类
普通局部内部类
静态成员内部类
静态成员代码块
Throwable
异常类
密封类
代码块
普通成员代码块
Mathlei
阿简
累计撰写
34
篇文章
累计收到
0
条评论
首页
栏目
随笔
JAVA
Mysql
JavaWeb
Tools
页面
壁纸
影院
统计
关于
友链
留言
搜索到
1
篇与
的结果
2024-08-09
泛型相关
泛型的基本操作泛型定义泛型即“参数化类型”,就是将具体的类型变成参数化类型。在声明一个泛型时,传递的是一个类型形参(Type Parameter),在调用时传递的是一个类型实参(Type Argument)。 参考形式参数泛型的好处示例代码:JavaBean:圆类型class Circle{ private double radius; public Circle(double radius) { super(); this.radius = radius; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } @Override public String toString() { return "Circle [radius=" + radius + "]"; } }比较器import java.util.Comparator; public class CircleComparator implements Comparator{ @Override public int compare(Object o1, Object o2) { //强制类型转换 Circle c1 = (Circle) o1; Circle c2 = (Circle) o2; return Double.compare(c1.getRadius(), c2.getRadius()); } }测试类public class TestGeneric { public static void main(String[] args) { CircleComparator com = new CircleComparator(); System.out.println(com.compare(new Circle(1), new Circle(2))); System.out.println(com.compare("圆1", "圆2"));//运行时异常:ClassCastException } }那么我们在使用如上面这样的接口时,如果没有泛型或不指定泛型,很麻烦,而且有安全隐患。因为在设计(编译)Comparator接口时,不知道它会用于哪种类型的对象比较,因此只能将compare方法的形参设计为Object类型,而实际在compare方法中需要向下转型为Circle,才能调用Circle类的getRadius()获取半径值进行比较。使用泛型:比较器:class CircleComparator implements Comparator<Circle>{ @Override public int compare(Circle o1, Circle o2) { //不再需要强制类型转换,代码更简洁 return Double.compare(o1.getRadius(), o2.getRadius()); } }测试类import java.util.Comparator; public class TestGeneric { public static void main(String[] args) { CircleComparator com = new CircleComparator(); System.out.println(com.compare(new Circle(1), new Circle(2))); // System.out.println(com.compare("圆1", "圆2"));//编译错误,因为"圆1", "圆2"不是Circle类型,是String类型,编译器提前报错,而不是冒着风险在运行时再报错 } }如果有了泛型并使用泛型,那么既能保证安全,又能简化代码。因为把不安全的因素在编译期间就排除了;既然通过了编译,那么类型一定是符合要求的,就避免了类型转换。声明类型变量\<T>声明类或接口时,在类名或接口名后面声明类型变量,这样的类或接口称为泛型类或泛型接口【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 父接口们】{ } 【修饰符】 interface 接口名<类型变量列表> 【implements 父接口们】{ } 例如: public class ArrayList<E> public interface Map<K,V>{ .... } 声明方法时,在【修饰符】与返回值类型之间声明类型变量,我们把声明(是声明不是单纯的使用)了类型变量的方法称为泛型方法【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{ //... } 例如:java.util.Arrays类中的 public static <T> List<T> asList(T... a){ .... }自定义泛型结构自定义泛型类和泛型接口在声明类或接口时,类或接口中定义某个成员时,该成员有些类型是不确定的,而这个类型需要在使用这个类或接口时才确定,可以使用泛型。声明泛型类语法格式:【修饰符】 class 类名<类型变量列表> { }注意:<类型变量列表>:可以是一个或多个类型变量,一般都是使用单个的大写字母表示。例如:<T>、<K,V> 等。当类或接口上声明了<类型变量列表>时,其中的类型变量不能用于静态成员上。示例: public class GerClass<T> { private T obj;//成员变量使用类上定义的类型变量T public T getObj() {//实例方法使用类上定义的类型变量T return obj; } public void setObj(T obj) { this.obj = obj; } //public static void test(T t){ } //此时类型变量T不能用在静态成员上 }声明泛型接口语法格式:【修饰符】 interface 接口名<类型变量列表> 【implements 父接口们】{ }示例://泛型接口 public interface GerInterface<T> { void show(T t); }泛型类和接口的子类或实现类泛型类和接口一样可以被继承或实现,一个类在继承父类或实现接口时分两种情况:子类或实现类明确泛型类的类型参数变量//定义实现类时,明确接口中声明的类型参数,此时实现类不再是泛型类 public class GerInterfaceImpl implements GerInterface<String> { @Override public void show(String t) { System.out.println(t); } }public class User implements Comparable<User>{ @Override public int compareTo(User u){ return 0; } }子类不明确泛型类的类型参数变量//定义实现类时,实现类不明确接口中声明的类型参数,实现类仍然是泛型类 public class GerInterfaceImpl<T> implements GerInterface<T> { @Override public void show(T t) { System.out.println(t); } }//ArrayList类实现了泛型接口,未明确泛型类型参数,依然是泛型类 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { }使用泛型类和接口在使用这种参数化的类与接口创建对象时,我们需要指定泛型变量的实际类型参数(必须是引用数据类型) public static void main(String[] args) { //使用泛型类或接口时,明确泛型参数类型为String GerInterface<String> gi = new GerInterfaceImpl<String>(); //gi.show(123);//泛型确定了String,这里编译失败 gi.show("hello"); }指定泛型实参时,必须左右两边类型参数一致。JDK1.7后支持简写形式,右边类型参数可以省略:GerInterface<String> gi = new GerInterfaceImpl<>();//省略右边泛型类型当使用参数化类型的类或接口时,如果没有指定泛型,相当于Object类型。//实现类确定了泛型类型 class Circle implements Comparable<Circle>{ private double radius; public Circle(double radius) { super(); this.radius = radius; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } @Override public String toString() { return "Circle [radius=" + radius + "]"; } @Override public int compareTo(Circle c){//参数类型确定 return Double.compare(radius,c.radius); } } //类型擦除: public class CircleComparator implements Comparator{ @Override public int compare(Object o1, Object o2) { //未指定泛型类型,默认为Object,使用时还要强制类型转换 Circle c1 = (Circle) o1; Circle c2 = (Circle) o2; return Double.compare(c1.getRadius(), c2.getRadius()); } }使用泛型类:public class TestGeneric{ public static void main(String[] args) { //语文老师使用时: Student<String> stu1 = new Student<String>("张三", "良好"); //数学老师使用时: //Student<double> stu2 = new Student<double>("张三", 90.5);//错误,必须是引用数据类型 Student<Double> stu2 = new Student<Double>("张三", 90.5); //英语老师使用时: Student<Character> stu3 = new Student<Character>("张三", 'C'); //错误的指定 //Student<Object> stu = new Student<String>();//错误的 } }继承泛型类并指定类型变量:class ChineseStudent extends Student<String>{//继承时确定了泛型类型 public ChineseStudent() { super(); } public ChineseStudent(String name, String score) { super(name, score); } }使用泛型类的子类:public class TestGeneric{ public static void main(String[] args) { //语文老师使用时: ChineseStudent stu = new ChineseStudent("张三", "良好"); } } 自定义泛型方法前面介绍了在定义类、接口时可以声明<类型变量>,在该类的方法和属性定义、接口的方法定义中,这些<类型变量>可被当成普通类型来用。使用泛型时,如果外界只关心某个方法,而不关心类其他的成员,那么可以只在该方法上声明泛型,方法泛型化,称为泛型方法。语法格式:【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{ //... }<类型变量列表>:可以是一个或多个类型变量,一般都是使用单个的大写字母表示。例如:、<K,V>等。静态方法也可以单独泛型化。区别泛型类或接口中的静态方法(不能使用泛型类或接口定义的泛型变量)。示例:public class GernericMethod { //泛型方法 public static <T> T getMsg(T t){ return t; } }public static void main(String[] args) { GernericMethod.getMsg("hello"); }类型变量的上限与泛型擦除类型变量的上限当在声明类型变量时,如果不希望这个类型变量代表任意引用数据类型,而是某个系列的引用数据类型,那么可以设定类型变量的上限。语法格式:<类型变量 extends 上限>如果有多个上限<类型变量 extends 上限1 & 上限2>如果多个上限中有类有接口,那么只能有一个类,而且必须写在最左边。接口的话,可以多个。如果在声明<类型变量>时没有指定任何上限,默认上限是java.lang.Object。class A implements Comparable { @Override public int compareTo(Object o) { return 0; } } public class MethodTest2 { //要求传入的值是Number和Comparable的孩子 static <T extends Number & Comparable> void cc(T t) { } //要求传入的值是A的孩子 static <T extends A> void mm(T t){ } //要求传入的值是可以比较的 public static <T extends Comparable> void show(T t) { } @Test public void test02() { Double d; cc(3.14); cc(100); // cc("张三"); A a = new A(); mm(a); } @Test public void test01() { A a = new A(); show(a); Integer i; show(6666); show(3.14); show("张三"); } }泛型擦除1没有指定泛型当使用参数化类型的类或接口时,会发生泛型擦除,自动按照最左边的第一个上限处理。如果没有指定上限,上限即为Object。package com.atguigu.limmit; import java.util.ArrayList; import java.util.Collection; public class TestErase { public static void main(String[] args) { NumberTools tools = new NumberTools(8,5); Number sum = tools.getSum();//自动按照Number处理 System.out.println("sum = " + sum); Number subtract = tools.getSubtract(); System.out.println("subtract = " + subtract); Collection coll = new ArrayList(); coll.add("hello"); coll.add(1); for (Object o : coll) {//自动按照Object处理 System.out.println(o); } } } 泛型存在于编译时类型通配符当我们声明一个变量/形参时,这个变量/形参的类型是一个泛型类或泛型接口,例如:Comparator类型,但是我们仍然无法确定这个泛型类或泛型接口的类型变量<T>的具体类型,此时我们考虑使用类型通配符。例如:这个学生类是一个参数化的泛型类,代码如下public class Student<T>{ private String name; private T score; public Student() { super(); } public Student(String name, T score) { super(); this.name = name; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public T getScore() { return score; } public void setScore(T score) { this.score = score; } @Override public String toString() { return "姓名:" + name + ", 成绩:" + score; } }<?>任意类型例如:我们要声明一个学生数组,可以存贮任意泛型的学生。测试类public class TestGeneric { public static void main(String[] args) { // 语文老师使用时: Student<String> stu1 = new Student<String>("张三", "良好"); // 数学老师使用时: // Student<double> stu2 = new Student<double>("张三", 90.5);//错误,必须是引用数据类型 Student<Double> stu2 = new Student<Double>("张三", 90.5); // 英语老师使用时: Student<Character> stu3 = new Student<Character>("张三", 'C'); Student<Object> stu4 = new Student<>("王五", new Object()); Student<Object>[] arr0 = new Student[4];//只能添加stu4 Student<?>[] arr = new Student[4]; arr[0] = stu1; arr[1] = stu2; arr[2] = stu3; arr[3] = stu4; } }<? extends 上限>用于设定通配符上限例如:我们要声明一个学生管理类,这个管理类要包含一个方法,找出学生数组中成绩最高的学生对象。要求学生的成绩的类型必须可比较大小,实现Comparable接口。学生管理类:class StudentService { //分数score的类型必须是实现了Comparable接口的 public static Student max(Student<? extends Comparable>[] arr){ Student<? extends Comparable> max = arr[0]; for (int i = 0; i < arr.length; i++) { if(arr[i].getScore().compareTo(max.getScore())>0){ max = arr[i]; } } return max; } }测试类public class TestGeneric { public static void main(String[] args) { Student<? extends Double>[] arr = new Student[3]; arr[0] = new Student<Double>("张三", 90.5); arr[1] = new Student<Double>("李四", 80.5); arr[2] = new Student<Double>("王五", 94.5); Student<? extends Comparable> max = StudentService.max(arr); System.out.println(max); } }<? super 下限>用于设定通配符下限现在要声明一个数组工具类,包含可以给任意对象数组进行从小到大排序,只要你指定定制比较器对象,而且这个定制比较器对象可以是当前数组元素类型自己或其父类的定制比较器对象数组工具类:class MyArrays{ public static <T> void sort(T[] arr, Comparator<? super T> c){ for (int i = 1; i < arr.length; i++) { for (int j = 0; j < arr.length-i; j++) { if(c.compare(arr[j], arr[j+1])>0){ T temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } }例如:有如下JavaBeanclass Person{ private String name; private int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } public Person() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "name=" + name + ", age=" + age; } } class Student extends Person{ private int score; public Student(String name, int age, int score) { super(name, age); this.score = score; } public Student() { super(); } public int getScore() { return score; } public void setScore(int score) { this.score = score; } @Override public String toString() { return super.toString() + ",score=" + score; } }测试类public class TestGeneric { public static void main(String[] args) { Student[] all = new Student[3]; all[0] = new Student("张三", 23, 89); all[1] = new Student("李四", 22, 99); all[2] = new Student("王五", 25, 67); MyArrays.sort(all, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.getAge() - o2.getAge(); } }); System.out.println(Arrays.toString(all)); MyArrays.sort(all, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getScore() - o2.getScore(); } }); System.out.println(Arrays.toString(all)); } }
2024年08月09日
6 阅读
0 评论
0 点赞