一.概述
上一篇学习了Java日志处理的相关内容,日志在工作中的用处非常多,学会如果进行打印日志我觉得还是跟实战经验有很大关系的,比如某个接口写完了,到底那些是需要我们打印出来的,参数结果等等。打得太多也很难看出关键的东西,最好的日志我觉得就是一眼就能看出关键问题所在,这才是完美的日志。Java日志处理
今天学习Java泛型技术,这个在平时里用的也很多但在我在工作中用到深处的可能并不是很多,定义泛型类也很少用到的基本就是用到集合中写泛型。但是作为学习还是要多学一些基本理论性的东西,对比较深的东西理解起来还是很有帮助的。泛型也是替代了到处使用object变量后,在进行转换对应类型时的不安全性。
二.基本概念
泛型的好处
泛型指的就是不确定类型,这就意味着编写的代码可以用不同的类型就行重用,
使用数组的时候需要确定数组大小后使用,但是如果想要扩展这个数组的使用,就要使用一个新数组将老数组添加进去来达到数组扩容的目的,Java中ArrayList集合解决了运行时无法动态扩容的问题。它的底层就是使用数组来实现的,后面讲到集合的时候会讲到。ArrayLIst<Object> list = new ArrayList<Object>
这个就是泛型数组语法。这是Java7之前的写法,前后保持一致,以后可以省略后面括号中的内容。它会自己检查对应的类型。这样我们在添加数据的使用可以进行类型的确定如果存在与类型不符合的编译器就会报错,这样避免了在运行时使用的时候产生类型转换的错误。
三.泛型的基本使用
1.定义简单泛型类
/**
* 泛型类
*/
public class GenericDemo {
public static void main(String[] args) {
String[] words = {"Mary", "had", "a"};
Pair<String> mm = ArrayAlg.minMax(words);
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
}
}
class ArrayAlg {
/**
* 返回一个实例化的泛型类
* @param a
* @return
*/
public static Pair<String> minMax(String[] a) {
if (a == null || a.length == 0) {
return null;
}
String min = a[0];
String max = a[0];
for (int i = 0; i < a.length; i++) {
if (min.compareTo(a[i]) > 0) {
min = a[i];
}
if (max.compareTo(a[i]) < 0) {
max = a[i];
}
}
return new Pair<>(min, max);
}
}
/**
* 定义泛型类
* 泛型类可以看作是普通类的工厂
* @param <T>
* T类型指定方法的返回类型以及域和局部变量的类型
* E:表示集合类型
* K,V:表的关键字和值的关键字
* T:表示任意类型
*/
class Pair<T> {
private T first;
private T second;
public Pair() {
first = null;
second = null;
}
public Pair(T first, T second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public void setFirst(T first) {
this.first = first;
}
public T getSecond() {
return second;
}
public void setSecond(T second) {
this.second = second;
}
}
2.泛型方法
/**
* 定义一个泛型方法
* 注意:类型变量放在修饰符的后面,返回类型的前面
* @param a
* @param <T>
* @return
*/
public static <T> T getMiddle(T... a) {
return a[a.length / 2];
}
3.类型变量的限定
在ArrayAlg中添加
/**
* <T extends Comparable>限定了只能允许实现了Comparable接口的类型才能调用
* T表示绑定类型的子类型,T和绑定类型可以是类,也可以是接口。
* T可以被多个类型限定 用&符号进行分隔
* @param a
* @param <T>
* @return
*/
public static <T extends Comparable> Pair<T> minmax(T[] a) {
if (a == null || a.length == 0) {
return null;
}
T min = a[0];
T max = a[0];
for (int i = 0; i < a.length; i++) {
if (min.compareTo(a[i]) > 0) {
min = a[i];
}
if (max.compareTo(a[i]) < 0) {
max = a[i];
}
}
return new Pair<>(min,max);
}
在main方法添加:
LocalDate[] birthdays = {
LocalDate.of(1999,10,1),
LocalDate.of(1998,9,1),
LocalDate.of(1997,8,1),
LocalDate.of(1996,7,1),
};
Pair<LocalDate> mm1 = ArrayAlg.minmax(birthdays);
System.out.println("min = " + mm1.getFirst());
System.out.println("max = " + mm1.getSecond());
四.泛型代码和虚拟机
1.类型擦除
/**
* 类型擦除后的类型信息
* 有限定类型的替换为限定类型,没有限定类型的用Object类型替换
*/
class Pair{
private Object first;
private Object second;
public Pair() {
first = null;
second = null;
}
public Pair(Object first, Object second) {
this.first = first;
this.second = second;
}
public Object getFirst() {
return first;
}
public void setFirst(Object first) {
this.first = first;
}
public Object getSecond() {
return second;
}
public void setSecond(Object second) {
this.second = second;
}
}
/**
* 如果类型为限定类型 则会以限定列表的第一个为限定类型
* 在必要时要向其余限定类型的接口进行强制类型转换
* 为了提高效率,应该将标签接口放在限定列表的末尾
* @param <T>
*/
class Pair<T extends Comparable & Serializable> implements Serializable{}
class Pair implements Serializable{
private Comparable a;
}
2.翻译泛型表达式
// 第一步:调用类型擦除的getFirst方法返回Object类型的对象
// 第二步:将Object类型的对象强制转换成String类型
Pair<String> pair = new Pair<>();
String first = pair.getFirst();
3.翻译泛型方法
/**
* 原类型
*/
class DateInterval extends Pair<LocalDate> {
@Override
public void setSecond(LocalDate second) {
if (second.compareTo(getFirst()) >= 0) {
super.setSecond(second);
}
}
/**
* 类型擦除后
* 类型擦除后会产生一个类型不同的同名方法这个方法是编译器生成的桥方法
* 解决了类型擦除后的多态问题
* 虚拟机会解决两个同名方法的问题
* @param second
*/
@Override
public void setSecond(Object second) {
super.setSecond(second);
}
}
总结:
- 虚拟机中没有泛型,只有普通类型的类和方法。
- 所有的类型参数都用它们的限定类型替换。
- 桥方法被合成来保持多态。
- 为保持类型安全性,必要时插入强制类型转换。
五.约束与局限性
1.不能用基本类型作为类型参数使用
因为泛型类型擦除后将使用Object类型作为替换,但是不能存储基本类型的值。
2.运行时类型查询只适用于原始类型
当使用instanceof的时候不能检查泛型
3.不能创建参数化类型的数组
4.不能实例化类型变量
5.不能在静态域或方法中引用类型变量。
6.不能抛出和捕获泛型类对象
7.可以消除对受查异常的检查***
8.注意擦除后的冲突
六.总结
这一篇主要说明了一下泛型的基本使用和一些限制,作者在使用泛型的场景基本就是集合中使用,和一些简单的泛型类,复杂的泛型用到的不是很多,毕竟水平没有达到很高,遇到的问题肯定也不是很多,所以这篇写的东西有些概念性,不是很清楚,希望大家不要介意。
下一篇学习泛型其他的特性。主要说的还是一些基本的使用方法,比如通配符等。
有些可能我理解的不够深刻,大家如果觉得我说的不够详细可以参考我的推荐书,详细的看一下。欢迎大家评论。第一时间我会回复大家。谢谢!