Java内部类和Lambda表达式
一、内部类
内部类也称为嵌套类,是在一个类的内部定义的类。通常一个内部类仅被其外部类使用时,同时也不想暴露出去,才定义为内部类。内部类不能定义在方法中。
实例内部类内部不允许定义静态成员。创建实例内部类的对象时需要使用外部类的实例变量.new 实例内部类类名( )。(即只有当有了外部类的实例,才能实例化 实例内部类的对象)
静态内部类用static定义,其内部允许定义实例成员和静态成员。
静态内部类的方法不能访问外部类的实例成员变量。
创建静态内部类的对象时需要使用new 外部类.静态内部类( )
注意静态和实例两种内部类的声明方式。
个人理解:因为实例内部类不能通过类名访问到,只能通过实例访问,而静态内部类就可以。
class Wrapper{
private int x=0;
private static int z = 0;
//内部静态类
static class A{
int y=0;
//可以定义静态成员,
//不能访问外部类的实例成员x,可访问外部类静态成员z
static int q=0;
int g() { return ++q + ++y + ++z; }
}
//内部实例类,不能定义静态成员,
//内部实例类可访问外部类的静态成员如z,实例成员如x
class B{
int y=0;
public int g( ) {
x++; y++;z++;
return x+y;
}
public int getX(){return x;}
}
}
public static void main(String[] args){
Wrapper w = new Wrapper(); //w.x = 0;
//创建内部静态类实例
Wrapper.A a = new Wrapper.A(); //a.y=0, a.q=0;
Wrapper.A b = new Wrapper.A(); //b.y=0, b.q=0;
a.g();
//a,b的实例成员彼此无关,因此执行完a.g()后,a.y = 1, b.y = 0;
//a,b共享静态成员q,所以a.q=b.q = 1;
//创建内部实例类实例
//不能用new Wrapper.B();必须通过外部类对象去实例化内部类对象
Wrapper.B c = w.new B(); //类型声明还是外部类.内部类
c.y=0;
c.g(); //c.y = 1 ,c.gextX() = 1
//在外部类体外面,不能通过内部类对象访问外部类成员,只能在内部类里面访问,
//编译器在这里只能看到内部类成员
// System.out.println(a.z); //错误
// System.out.println(c.x); //错误
//不能通过c直接访问外部类的x,可通过c.gextX()
System.out.println(c.getX());
}
一个内部类被编译成名为OuterClassName$InnerClassName的类
内部类作用:如果一个类A仅仅被某一个类B使用,且A无需暴露出去,可以把A作为B的内部类实现,内部类也可以避免名字冲突:因为外部类多了一层名字空间的限定。例如类Wrapper1、Wrapper2可以定义同名的内部类A而不会导致冲突。
匿名内部类:没有名字的内部类
匿名内部类可以简化变成,简化时视同匿名内部类的父类或者接口代替匿名内部类。
//内部类
public class HandleWindowEvent extends Application {
public void start(Stage primaryStage) throws Exception {
//其它代码省略
primaryStage.setOnCloseRequest(new WindowEventHandlerClass ( ) );
}
class WindowEventHandlerClass implements
EventHandler<WindowEvent> {
public void handle(WindowEvent e) { //处理语句 }
}
}
//匿名内部类
public class HandleWindowEvent extends Application {
public void start(Stage primaryStage) throws Exception {
//其它代码省略
primaryStage.setOnCloseRequest(
new EventHandler<WindowEvent>( ) {
public void handle(WindowEvent e) { //处理语句 }
}
);
//匿名内部类没有类名,用这个类所实现的接口作为匿名内部类的类名 new
}
//new一个内部匿名类对象时,new 后面直接用这个匿名内部类的父类或者所实现接口作为类型
匿名内部类总是使用父类的无参构造方法产生实例,对于接口使用Object( )。
匿名内部类必须实现父类或者接口的所有抽象方法,事件处理接口通常只有1个方法。
一个匿名内部类被编译成OuterClassName$n.class,如Test$1.class, Test$2.class
二、Lambda表达式
- Lambda表达式可以进一步简化事件处理的程序编写
- 只有一个方法的接口称为功能接口(函数式接口),每个 Lambda 表达式都能隐式地赋值给函数式接口,lamda表达式中的{ }就是函数式接口中接口方法的方法体。
- Lambda表达式本质上更像匿名函数。
- Java里规定Lambda表达式只能赋值给函数式接口。
- Lambda表达式的语法为:
(type1 para1, …, typen paran)->expression 或者
(type1 para1, …, typen paran)->{ 一条或多条语句}
- 当把Lambda表达式赋值给函数式接口时, Lambda表达式的参数的类型是可以推断的;如果只有一个参数,则可以省略圆括弧。从而使Lambda表达式简化为:
e->处理e的expression 或者
e->{ 处理e的statements; }
(int a, int b) -> { return a + b; }
() -> System.out.println("Hello World")
(String s) -> { System.out.println(s); }
() -> 42
() -> { return 3.1415 ;}
Lambda 表达式的结构:
- 一个 Lambda 表达式可以有零个或多个参数
- 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同(当可以推断类型时)
- 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
空圆括号代表参数集为空。例如:() -> 42 - 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> {return a*a;}
- Lambda 表达式的主体可以是表达式或者是block,如果是表达式,不能有{};如果是block,则必须加 { }
每个 Lambda 表达式都能隐式地赋值给函数式接口:
- Runnable接口就是函数式接口,里面定义接口方法void run( ),我们可以通过 Lambda 表达式创建一个接口实例 。
- 上面语句的含义是:将一个实现了Runnable接口的类的实例赋值给Runnable接口引用r, Lambda 表达式的主体就是接口方法void run( )的具体实现
- 当不是显式赋值给函数式接口时,编译器会自动解释这种转化:
new Thread(
() -> System.out.println("hello world")
).start();
- 在上面的代码中,编译器会自动推断:根据线程类的构造函数签名 public Thread(Runnable r) { },将该 Lambda 表达式赋给 Runnable 接口。
lambda表达式和Runnable接口天然合适
//定义一个函数式接口
public interface WorkerInterface {
public void doSomeWork();
}
public class WorkerInterfaceTest {
public static void exec(WorkerInterface worker) {
worker.doSomeWork();
}
public static void main(String [] args) {
//invoke doSomeWork using Annonymous class
//匿名类
exec( new WorkerInterface() {
@Override public void doSomeWork() {
System.out.println("Worker invoked using Anonymous class"); }
} );
//invoke doSomeWork using Lambda expression
//Lambda表达式
exec( () -> System.out.println("Worker invoked using Lambda expression") );
}
}
//Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = 0;
for(Integer n : list) {
int x = n * n;
sum = sum + x;
}
System.out.println(sum);
//New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);
补充
闭包的理解:闭包(Closure)并不是一个新鲜的概念,很多函数式语言中都使用了闭包。例如在JavaScript中,当你在内嵌函数中使用外部函数作用域内的变量时,就是使用了闭包。用一个常用的类比来解释闭包和类(Class)的关系:类是带函数的数据,闭包是带数据的函数。