美文网首页Java 之旅
初级11 - 接口与抽象类(下)

初级11 - 接口与抽象类(下)

作者: 晓风残月1994 | 来源:发表于2019-08-06 21:37 被阅读0次

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));
    }
}

参考:
Java 内部类与外部类的互访使用小结

相关文章

网友评论

    本文标题:初级11 - 接口与抽象类(下)

    本文链接:https://www.haomeiwen.com/subject/lfaidctx.html