内部类是定义在一个类中的类,内部类有以下特点
示例,下述代码中创建了一个类,里面有一个内部类,在内部类定义了一个定时器,定时输出当前时间,在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);
}
要在类外部访问内部类需要通过OuterClass.InnerClass语法
示例如下
TalkingClock talkingClock = new TalkingClock(1000,true);
TalkingClock.TimerPrint timerPrint = talkingClock.new TimerPrint();
定义在方法内部的类,局部类不能用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);
}
局部内部类出了访问外部类变量,还可以访问局部变量,但是这部分变量要声明为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);
}
匿名内部类可以使代码更简洁,在局部内部类的基础上,如果只需要使用一次本地类,不需要单独声明一个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();
}
}
如果内部类不需要引用外部类对象,则将内部类声明为static,以便取消产生的引用
class ArrayLog{
@Data
public static class Pair{
public Pair(){}
public void sayHello(){}
}
}
ArrayLog.Pair pair = new ArrayLog.Pair();
pair.sayHello();
lambda表达式是一个匿名函数,通常情况下,我们会将方法作为一个参数传递给另一个方法,匿名内部类可以在不为其指定名称的情况下实现基类,但是看起来任然很烦琐,通过lambda表达式,可以优化代码结构,更加易懂且易于维护,lambda可用于进行函数式变成
对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式,这种接口称为函数式接口,在该接口上可以通过 @FunctionalInterface
注解声明这是一个函数式接口,java将常用的函数式接口,放在了java.util.function包下
例如Java的Runnable接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
接口名 | 参数类型 | 返回值类型 | 抽象方法名 | 描述 | 其他方法 |
---|---|---|---|---|---|
Runnable | 无 | void | run | 运行线程任务 | |
Supplier | 无 | T | get | 提供一个T类型的值 | |
Consumer | T | void | accept | 处理一个T类型的值 | addThen |
BiConsumer<T,U> | T,U | void | accept | 处理T和U类型的值 | addThen |
Function<T,R> | T | R | apply | 有一个T类型的参数的函数 | componse,addThen,identily |
BiFunction<T,U,R> | T,U | R | apply | 有T和U类型的参数的函数 | addThen |
UnaryOperator | T | T | apply | 类型T上的一元操作符 | componse,addThen,identily |
BinaryOperator | T,T | T | apply | 类型T上的二元操作符 | addThen,maxBy,minBy |
Predicate | T | boolean | test | 布尔值函数 | and,or,negate,isEqual |
BiPredicate<T,U> | T,U | boolean | test | 有两个参数的布尔值函数 | and,or,egate |
lambda的方法引用不是一个对象,为一个类型是函数式接口的变量赋值时会生成一个对象
通过 ::
关键字传递方法或构造函数的引用,存在以下三种形式
第一种情况种,方法引用等价于向方法传递参数的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写为方法引用
构造器引用与方法名类似,但是方法名是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);
}