面向对象
重载
JVM在重载方法中,选择合适的目标方法的顺序如下:
- 精确匹配
- 如果是基本数据类型,则转换成更大表示范围的基本类型
- 通过自动装箱与拆箱
- 通过子类向上转型继承路线依次匹配
- 通过可变参数匹配
泛型
泛型可以定义在类,接口和方法中。
E代表Element,用于集合中的元素;T表示某个类;K代表Key,V代表Value,用于键值对元素。
- 尖括号里的每个元素都指代一种未知类型。即使是String出现在尖括号里面就不是
java.lang,String
了,而仅仅是一个代号。类名后方定义的泛型和方法前定义的是两个指代,可以完全不同。 - 尖括号的位置必须在类名之后或方法返回值之前。
- 泛型在定义处只具备执行Object方法的能力,可以强制转型。
- 编译后的字节码指令,会有类型擦除(将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型))。
- 泛型指示的都是Object(在使用的时候都是以Object来处理的,包括使用的方法啥的)。除非使用了后面的
<? extends T>
,那么由于传入的是T以及T的子类,所以可以用T类中的方法。
好处:
- 类型安全。放置的是什么,取出来的就是什么,不会有
ClassCastException
。 - 提升可读性。
- 代码重用。
在类中的应用
public class GenericsDemo06 {
public static void main(String[] args) {
Point<String> p = new Point<>();
p.setVar("it");
System.out.println(p.getVar().length());
}
}
class Point<T> {
private T var;
public T getVar() {
return var;
}
public void setVar(T var) {
this.var = var;
}
}
在接口中的应用
public class GenericsDemo24 {
public static void main(String[] args) {
Info<String> i = new InfoImpl<>("Tom");
System.out.println(i.getVar());
}
}
interface Info<T> {
T getVar();
}
class InfoImpl<T> implements Info<T> {
private T var;
public InfoImpl(T var) {
this.var = var;
}
public void setVar(T var) {
this.var = var;
}
@Override
public T getVar() {
return null;
}
}
在方法中的应用
泛型方法,是在调用方法的时候指明泛型的具体类型。
定义泛型方法时,必须在返回值前边加一个<T>
,来声明这是一个泛型方法,持有一个泛型T
,然后才可以用泛型T作为方法的返回值。
为什么要使用泛型方法呢?因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活。
在集合的应用
List<?>
是一个泛型,在没有赋值前,表示它可以接受任何类型的集合的赋值,赋值之后就不能随便往里添加元素了,但是可以删除和清除元素。
Collection是最顶层的接口,如果参数是泛型限制的集合(Collection),可以接受非泛型和泛型的集合(List等一系列继承自Collection的接口)。当然如果一个类实现了List等接口也是可以传入的,因为反正里面也是调用这个接口的方法,那么实现了继承自Collection的接口的类也一定有对应的方法。
如下:
public boolean addAll(Collection<? extends E> c) {...}
可以接受JSONArray
,因为:
public final class JSONArray extends AbstractJSOn implements JSON, List {}
ps
:可以将Integer[]
赋值给Object[]
,但是不能将List<Integer>
赋值给List<objetc>
。
原因:数组是协变的,所以可以将子类型数组赋值给超类型数组。但是泛型类型是不变的,所以不能将子类赋值给超类型泛型。(简单理解就是:声明的时候,指定List<Object>
后,中的T就是Object了,由于泛型是不变的,所以不能将T赋值成Integer
)。编译时类型检查,因为如果是可变的,那么将List<Object>
改为指向List<Integer>
,就有可能会添加不是Integer
类型的元素。
-
问号在正则表达式中可以匹配任何字符,
List<?>
称为通配符集合,可以接受任何类型的集合引用赋值,不能添加任何元素,可以remove和clear。List<Integer> a3 = new ArrayList<>(); List<?> a4 = a3;
List<?>
一般用作参数来接收外部的集合,或者返回一个不知道具体元素类型的集合。 -
<? extends T>
适用于消费集合元素为主的场景。(Get First)可以给赋值(=)任何T以及T子类的集合,上界为T,取出来的类型会向上强制转型为T。
null 可以表示任何类型,所以除null外,任何元素都不得添加进
<? extends T>
集合内。可以返回(get)带类型的元素,但是只能返回T以及T父类对象。(因为能保证上限是T,返回T及父类不会有问题)
-
<? super T>
适用于生产集合元素为主的场景。(Put First)可以给赋值(=)任何任何T以及T的父类集合,下界为T。
可以添加(add)元素,但是只能添加T以及T的子类集合。(因为只能保证最低是T,所以不能添加T以上的,只能添加T及T以下的)
可以返回(get)元素,但是泛型丢失,只会返回Object。
注意:如果T是一个接口,那么这里还可以指 实现了T的父类或子类接口的实现类
如下:这里的意思是实现了Comparable或则其子类这个接口的类E,而不是单纯指E是个接口。
private <E extends Comparable<? super E>> E max(List<? extends E> e1) {
if (e1 == null) {
return null;
}
Iterator<? extends E> iterator = e1.iterator();
E result = iterator.next();
while (iterator.hasNext()) {
E next = iterator.next();
if (next.compareTo(result) > 0) {
result = next;
}
}
return result;
}