初级11 - 接口与抽象类(下)
2019-08-06 本文已影响0人
晓风残月1994
1. 内部类
用来实现更加精细的封装,可以和外部类之间访问更方便,包括:
- 内部类
- 静态内部类
内部类和静态内部类的区别:
- 非静态内部类会和外部类的实例相绑定,可以无障碍调用外部类的实例方法;
- 而静态内部类不会这样,所以不能直接调用外部类的实例方法。
public class Outer {
private void log() {
System.out.println("ok");
}
private class InnerA {
// 编译器会偷偷注入一个外部类的实例/对象
// final Outer this$0;
log(); // ok
}
private static class InnerB {
log(); // 不 ok
}
}
原则:永远使用静态内部类,除非编译报错(参考 《Effective Java》)
如果因为内部类中访问外部类的实例方法时编译报错了,则有两种做法:
- 此时考虑使用非静态内部类
- 曲线救国,对内部类实例化时传入外部类的实例,并保存成内部类的实例成员,然后通过这个外部实例的引用来调用其上的实例方法,做法如下(仅作演示,具体使用略):
public class Outer {
private void log() {
System.out.println("ok");
}
private static class Inner {
private Outer outer;
public Inner(Outer outer) {
this.outer = outer;
}
outer.log(); // 这次 ok 了
}
}
2. 匿名内部类
直接通过 new 的方式创建的无名类
class XXX implements Predicate<Object> {
@Override
public boolean test(Object obj) {
return false;
}
}
当new XXX()
进行实例化时,等价于直接使用匿名类:
public class Outer {
// ...
new Predicate<Object>() {
@Override
public boolean test(Object obj) {
return true;
}
}
}
注意这种语法形式!可以简单理解为语法糖,要记住接口是不能被实例化的,只是被实现了。
编译成字节码后,上面的匿名内部类会用Outer$1.class
来表示文件名。
来看下面的例子:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class NameCollector implements Consumer<User> {
private final List<String> names = new ArrayList<>();
@Override
public void accept(User user) {
names.add(user.getName());
}
public List<String> getNames() {
return names;
}
}
使用匿名内部类进行优化后,就不需要上面具名的实现类了:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class User {
/** 用户ID,数据库主键,全局唯一 */
private final Integer id;
/** 用户名 */
private final String name;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
// 这里不再使用 NameCollector 类,而是改写成匿名内部类
// 使得代码更加集中,更加容易阅读
public static List<String> collectNames(List<User> users) {
// NameCollector collector = new NameCollector();
// users.forEach(collector);
// return collector.getNames();
final List<String> names = new ArrayList<>();
users.forEach(new Consumer<User>(){
@Override
public void accept(User user) {
names.add(user.getName());
}
});
return names;
}
public static void main(String[] args) {
List<User> users = Arrays.asList(new User(1, "a"), new User(2, "b"));
System.out.println(collectNames(users));
}
}