前言
最近研究RxSwift框架发现里面用到了匿名内部类的思想,今天做了一下总结,文章最后有demo下载
匿名内部类的作用
java中有匿名内部类的语法,可以做到的是,就是想要重写一个类中的方法,并且执行,但是不想重新定义一个类,因为只调用一次,或者调用多次,但是每次调用的实现过程都不同,这时候就可以使用匿名内部类
java中的匿名内部类
public class HelloWorldAnonymousClasses {
/**
* 包含两个方法的HelloWorld接口
*/
interface HelloWorld {
public void greet();
public void greetSomeone(String someone);
}
public void sayHello() {
// 1、局部类EnglishGreeting实现了HelloWorld接口
class EnglishGreeting implements HelloWorld {
String name = "world";
public void greet() {
greetSomeone("world");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hello " + name);
}
}
HelloWorld englishGreeting = new EnglishGreeting();
// 2、匿名类实现HelloWorld接口
HelloWorld frenchGreeting = new HelloWorld() {
String name = "tout le monde";
public void greet() {
greetSomeone("tout le monde");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
// 3、匿名类实现HelloWorld接口
HelloWorld spanishGreeting = new HelloWorld() {
String name = "mundo";
public void greet() {
greetSomeone("mundo");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hola, " + name);
}
};
englishGreeting.greet();
frenchGreeting.greetSomeone("Fred");
spanishGreeting.greet();
}
public static void main(String... args) {
HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
myApp.sayHello();
}
}
运行结果:
1 Hello world
2 Salut Fred
3 Hola, mundo
swift中实现匿名内部类
理想中的跟java一样的swift匿名内部类(语法编译错误)
swift中并没有匿名内部类的语法,就是 类名 变量 = new 类名() { 重写类内的内容 } 调用的时候 对象.方法名()
按照java 的做法, swift,按照执行匿名闭包的做法应该写成如下,但是这样写编译器会报错,
关于匿名闭包的用法,请参照我这篇文章:
swift的匿名闭包用法教程
let 变量 = class 类名
protocol PersonProtocol2 {
func hello(str: String) -> ()
}
let obj = class Person2 : PersonProtocol2 { //这样编译会报错,不能定义类的时候再用()执行类创建对象,赋值给变量
func hello(str: String) -> () {
}
}()
真实的匿名内部类
做法1
通过协议,让类实现协议,并且实现协议中的方法,再建立类对象,并且执行类对象的方法,再赋值给闭包表达式,执行的时候是执行闭包表达式,如下执行的时候,执行:closure1(),这个做法是把整个执行过程包装起来,调用的是闭包变量,下面closure1和 closure2分别实现2个不同的执行过程,每个内部类的事先只调用一次,所以可以用匿名内部类,外部也不会被污染
protocol PersonProtocol {
func hello()
}
let closure1: () -> () = {
class NotSoAnonymousClass : PersonProtocol {
func hello() {
print(#line, #function, "Hello")
}
}
let object = NotSoAnonymousClass()
object.hello()
}
let closure2 = {
class NotSoAnonymousClass : PersonProtocol {
func hello() {
print(#line, #function, "Hello Two")
}
}
let object = NotSoAnonymousClass()
object.hello()
}
调用方法,我放到了2个按钮里面,点击按钮执行
closure1()
closure2()
执行结果:
16 hello() Hello
25 hello() Hello Two
做法2
下面的做法跟上面差不多,跟java的做法基本一样,java是 new 出class的时候在{}里面重写了类里的实例方法.swift按照java的思路来写,让类实现协议,并且实现协议中的方法,但是swift语法不能在定义class类的时候,马上用()括号执行调用他,再复制给变量,语法不行,所以要先定义一个类.
protocol PersonProtocol2 {
func hello(str: String) -> ()
}
/*
let obj = class Person2 : PersonProtocol2 { //这样编译会报错,解决方法如下
func hello(str: String) -> () {
}
}()
*/
//让类Person继承协议,并且重写空的方法,真正要调用有内容的方法的时候,我们在下面的匿名内部类里实现
class Person : PersonProtocol2 {
func hello(str: String) -> () {
}
}
var obj1:Person = { //这里obj1:PersonProtocol2 也可以,因为都有hello方法,但是不知不指定类型,
class Anonymous : Person { //在闭包内定义的类,是内部类,外部看不见这个名字,就变成了匿名内部类,这个实现方法,跟java最像,返回的也是一个实力对象
override func hello(str: String) -> () {
print("开口说:",str)
}
}
return Anonymous()
}()
调用方法:
obj1.hello(str: "你好")
执行结果:
开口说: 你好
做法3
- 下面的方法是建立一个空的类,当闭包变量被赋值之前,表达式右边的闭包内容,修改了对象里的block变量fn,并且在init初始化的时候,给block变量传入block的执行内容. 如果看着比较拗口就直接看下面代码
- 在类外执行的方法,实际上是调用对象的block
- 注意EmptyClass没有继承任何类,所以不用写super.init()
- EmptyClass2继承了NSObject,所以在init()中要先调用super.init,然后才能创建self对象.具体下面的closure3闭包变量的调用原理,我在代码里写了详细的注释,按照注释一步步看.
//下面的方法是建立一个空的类,在init初始化的时候,给block变量传入block的执行内容,执行的方法,实际上是调用对象的block
class EmptyClass {
var fn: () -> () = { }// 这个类里面的fn是个block变量,并且用空的表达式初始化,如果不初始化,不能就必须在init中初始化这个变量
//init方法,的参数是传入一个block,参数是传入当前类的对象,返回值也是当前类的对象
init(callback:(EmptyClass)->EmptyClass) {
callback(self) //这里是省略写法,完整写法是 return callback(self)
//给block callback传入的值是
}
}
//closure3闭包初始化的时候,因为init已经获取了对象,所以直接可以修改对象下的block变量
let closure3 = EmptyClass { obj
in
obj.fn = { print("closure3 run") }
return obj
}
//继承自NSObject的类
class EmptyClass2:NSObject {
var fn: () -> () = { }
init(callback:(EmptyClass2)->EmptyClass2) {
super.init()//因为本来有父类,就是NSObject,所以要使用之前必须先调用父类的指定初始化器
callback(self)
//给block callback传入的值是
}
}
let closure4 = EmptyClass2 { obj in
obj.fn = { print("closure4 run") }
return obj
}
//类中的block变量带参数列表的情况如下
class EmptyClass3 {
var fn: (_ str: String) -> () = { str in }//带参数的block变量
init(callback: (EmptyClass3) -> EmptyClass3) {
callback(self)
}
}
let closure5 = EmptyClass3 { obj in
obj.fn = { str
in
print("str=",str)
}
return obj
}
调用方法:
closure3.fn()
closure4.fn()
closure5.fn("abc123")
执行结果:
closure3 run
closure4 run
str= abc123
总结
匿名内部类这种做法的目的:
1是为了偷懒,不想每次调用一个重写的实力方法都重写继承一个类再重写
2.避免污染类外的代码,内部类,保证了外部不会访问到,并且出现重名,因为作用域不一样,也会调用不同的方法
3.某些框架大佬再用,如果看不懂,就理解不了思想
demo代码下载
下面是我上面写的demo代码,使用xcode 12.3打开项目:
demo代码