Java基础--内部类和lambda表达式

Updated on with 0 views and 0 comments

一、嵌套类

1.1 内部类

1.1.1 概念

内部类是定义在一个类中的类,内部类有以下特点

  • 内部类的方法可以访问该类定义所在的作用域中的数据,包括私有数据
  • 内部类可以对同一个包中的其他类隐藏起来

示例,下述代码中创建了一个类,里面有一个内部类,在内部类定义了一个定时器,定时输出当前时间,在TimerPrint内部类中,我们可以访问到TalkingClock的数据(包括静态变量、静态方法、普通变量、普通方法)

class TalkingClock{
    private int interval;
    private boolean beep;
    public static String NAME = "龙";

    public TalkingClock(int interval, boolean beep){
        this.interval = interval;
        this.beep = beep;
    }

    public void start(){
        ActionListener actionListener = new TimerPrint();
        Timer t = new Timer(interval,actionListener);
        t.start();
    }
    public class TimerPrint implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            Date date = new Date();
            System.out.println(date);
            if(beep) Toolkit.getDefaultToolkit().beep();
        }
    }
}
    public static void main(String[] args){
        TalkingClock clock = new TalkingClock(1000,true);
        clock.start();
        JOptionPane.showMessageDialog(null,"Quit Program");
        System.exit(0);
    }

1.1.2 在类外部访问内部类

要在类外部访问内部类需要通过OuterClass.InnerClass语法

示例如下

        TalkingClock talkingClock = new TalkingClock(1000,true);
        TalkingClock.TimerPrint timerPrint = talkingClock.new TimerPrint();

1.2 局部内部类

1.2.1 概念

定义在方法内部的类,局部类不能用public或prvate等权限修饰符说明,他的作用域在声明这个局部内部类的块中,可以将类对外部完全隐藏

class TalkingClock{
    private int interval;
    private boolean beep;
    public static String NAME = "龙";

    public TalkingClock(int interval, boolean beep){
        this.interval = interval;
        this.beep = beep;
    }

    public void start(){
         class TimerPrint implements ActionListener {
            @Override
            public void actionPerformed(ActionEvent e) {
                Date date = new Date();
                System.out.println(date);
                if(beep) Toolkit.getDefaultToolkit().beep();
            }
        }
        ActionListener actionListener = new TimerPrint();
        Timer t = new Timer(interval,actionListener);
        t.start();
    }

}
    public static void main(String[] args){
        TalkingClock talkingClock = new TalkingClock(1000,true);
        talkingClock.start();

        JOptionPane.showMessageDialog(null,"close dialog");
        System.exit(0);
    }

1.2.2 访问局部变量

局部内部类出了访问外部类变量,还可以访问局部变量,但是这部分变量要声明为final类型

class TalkingClock{
    public void start(int interval,boolean beep){
        class TimerPrint implements ActionListener {
            @Override
            public void actionPerformed(ActionEvent e) {
                Date date = new Date();
                System.out.println(date);
                if(beep) Toolkit.getDefaultToolkit().beep();
            }
        }
        ActionListener actionListener = new TimerPrint();
        Timer t = new Timer(interval,actionListener);
        t.start();
    }
}
    public static void main(String[] args){
        TalkingClock talkingClock = new TalkingClock();
        talkingClock.start(1000,true);

        JOptionPane.showMessageDialog(null,"close dialog");
        System.exit(0);
    }

1.3 匿名内部类

1.3.1 概念

匿名内部类可以使代码更简洁,在局部内部类的基础上,如果只需要使用一次本地类,不需要单独声明一个class给他命名,匿名内部类,不能写构造函数

在下面的示例代码中,我们不在声明class去实现ActionListener接口,而是之间通过new创建,并重写了actionPerformed方法

class TalkingClock{
    public void start(int interval,boolean beep){
        ActionListener actionListener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Date date = new Date();
                System.out.println(date);
                if(beep) Toolkit.getDefaultToolkit().beep();
            }
        };
        Timer t = new Timer(interval,actionListener);
        t.start();
    }
}

1.4 静态内部类

1.4.1 概念

如果内部类不需要引用外部类对象,则将内部类声明为static,以便取消产生的引用

  • 内部类不需要访问外围类应用时使用静态内部类
  • 声明在接口中的内部类,自动成为public statwaiclass ArrayLog{

1.4.2 在外部中访问静态内部类

class ArrayLog{
    @Data
    public static class Pair{
        public Pair(){}
        public void sayHello(){}
    }
}
ArrayLog.Pair pair = new ArrayLog.Pair();
        pair.sayHello();

1.4.3 静态内部类与内部类的区别

  • 静态内部类无法访问外部类中的变量及方法,要访问的话需要new一个外部类,但是静态类可以访问静态变量和静态方法
  • 普通内部类作为外部类的一个成员而存在,在普通内部类中可以直接访问外部类属性,调用外部类方法
  • 如果外部类要访问内部类的属性和方法,则需要new一个内部类对象
  • 如果其他类要访问内部类,需要在外部类中建一个内部类对象作为属性,外部类可以通过属性调用内部类的方法或者访问普通内部类属性,或者通过外部类实例引用.new 内部类创建一个内部类实例
  • 如果其他类要访问静态内部类,直接创建一个静态内部类对象即可,或者通过new外部类.内部类获取内部类实例

二、Lambda表达式

lambda表达式是一个匿名函数,通常情况下,我们会将方法作为一个参数传递给另一个方法,匿名内部类可以在不为其指定名称的情况下实现基类,但是看起来任然很烦琐,通过lambda表达式,可以优化代码结构,更加易懂且易于维护,lambda可用于进行函数式变成

2.1 函数式接口

对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式,这种接口称为函数式接口,在该接口上可以通过 @FunctionalInterface注解声明这是一个函数式接口,java将常用的函数式接口,放在了java.util.function包下

例如Java的Runnable接口

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

2.2 常用的函数式接口

接口名参数类型返回值类型抽象方法名描述其他方法
Runnablevoidrun运行线程任务
SupplierTget提供一个T类型的值
ConsumerTvoidaccept处理一个T类型的值addThen
BiConsumer<T,U>T,Uvoidaccept处理T和U类型的值addThen
Function<T,R>TRapply有一个T类型的参数的函数componse,addThen,identily
BiFunction<T,U,R>T,URapply有T和U类型的参数的函数addThen
UnaryOperatorTTapply类型T上的一元操作符componse,addThen,identily
BinaryOperatorT,TTapply类型T上的二元操作符addThen,maxBy,minBy
PredicateTbooleantest布尔值函数and,or,negate,isEqual
BiPredicate<T,U>T,Ubooleantest有两个参数的布尔值函数and,or,egate

2.3 方法及构造函数引用

2.3.1 方法引用

lambda的方法引用不是一个对象,为一个类型是函数式接口的变量赋值时会生成一个对象

通过 ::关键字传递方法或构造函数的引用,存在以下三种形式

  • object::instanceMethod
  • Class:instanceMethod
  • Class:staticMethod

第一种情况种,方法引用等价于向方法传递参数的lambda表达式,例如 System.out::println,对象是System.out方法表达式等价于 x-System.out.println(x)

如下述代码中,两个输出字符串的调用是等价的

    public static void main(String[] args){
        List<String > strings = new ArrayList<>();
        strings.add("111");
        strings.forEach(s->System.out.println(s));
        strings.forEach(System.out::println);
    }

第二种情况第一个参数会成为方法的隐式参数,例如 String::compareToIgnoreCase等同于 (x,y)->x.compareToIgnoreCase(y)

如下述代码中,两个排序传入的方法是等价的

public static void main(String[] args){
        List<String > strings = new ArrayList<>();
        strings.add("222");
        strings.add("111");
        strings.sort(String::compareToIgnoreCase);
        strings.sort((x,y)->x.compareToIgnoreCase(y));
        System.out.println(strings);
    }

第三种情况,所有参数都传递到了静态方法,例如 Math::pow等价于 (x,y)->Math.pow(x,y)

例如下面两段代码是等价的

    public static void main(String[] args){
        BiFunction<Double, Double, Double> pow = Math::pow;
        Double ret = pow.apply(2d, 2d);
        System.out.println(ret);
    }
class MathOperation{
    double pow(double x,double y){
        return Math.pow(x,y);
    }
}
public static void main(String[] args){
    MathOperation mathOperation = new MathOperation();
    double ret = mathOperation.pow(2d,2d);
    System.out.println(ret);
}

当lambda表达式仅调用方法而不进行其他操作,才将lambda写为方法引用

2.3.2 构造器引用

构造器引用与方法名类似,但是方法名是new

如下代码所示

@Data
class Person{
    public Person(String name){
        this.name = name;
    }
    private String name;
}

public static void main(String[] args){
        List<String> names = new ArrayList<>();
        names.add("龙");
        List<Person> personList = names.stream().map(Person::new).collect(Collectors.toList());
        System.out.println(personList);
    }

2.4 变量作用域

  • lambda表达式,可以访问表达式外部的局部变量,但是该变量必须是最终的值,不能再改变,该变量可以显示的使用final声明
  • 访问类的静态变量和字段

标题:Java基础--内部类和lambda表达式
作者:wenyl
地址:http://www.wenyoulong.com/articles/2023/06/27/1687854356869.html