此处将为大家介绍关于Swiftvs.Kotlin漫谈系列之类与继承的详细内容,并且为您解答有关kotlinswift区别的相关问题,此外,我们还将为您介绍关于2022Kotlin中文开发者大会倒计时,
此处将为大家介绍关于Swift vs. Kotlin 漫谈系列之类与继承的详细内容,并且为您解答有关kotlin swift区别的相关问题,此外,我们还将为您介绍关于2022 Kotlin 中文开发者大会倒计时,迎接周末两天专属 Kotlin 的技术盛宴!、Android Kotlin RXKotlin Room - 错误 Kotlin.unit、java – 更新到Android Studio 3.1后我遇到了这个错误:找不到org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0、Kotlin 1.3 正式发布:带来 Kotlin/Native Beta 和协程的有用信息。
本文目录一览:- Swift vs. Kotlin 漫谈系列之类与继承(kotlin swift区别)
- 2022 Kotlin 中文开发者大会倒计时,迎接周末两天专属 Kotlin 的技术盛宴!
- Android Kotlin RXKotlin Room - 错误 Kotlin.unit
- java – 更新到Android Studio 3.1后我遇到了这个错误:找不到org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0
- Kotlin 1.3 正式发布:带来 Kotlin/Native Beta 和协程
Swift vs. Kotlin 漫谈系列之类与继承(kotlin swift区别)
Kotlin 君和 Swift 君在一个团队一起开发已经很久了,由于平台的差异性,他们经常会进行一些技术上的交流(PK),《Kotlin vs. Swift 漫谈》系列就是他们在互相切磋是的语录。内容会由简及深,慢慢深入。
技术漫谈
Kotlin:
Swift 君,你好。
Swift:
Kotlin 君,你好。,干嘛笑的那么坏。
Kotlin:
没有什么,你最近是不是胖了。
Swift:
赶紧进入正题吧,说说你们的类是怎么定义的。
Kotlin:
我们定义类和 Java 差不多,也是用 class
声明一个类,不过 Kotlin 里面如果类实体没有什么内容的话,可以不要大括号。
Swift:
哦哦,不要大括号,那好简洁啊,我们不行。不过我们实例化的时候可以不用 new 关键字。也就是 Swift 程序员不用 new 就可以有对象了。
Kotlin:
嘿,正经点,你都是有老婆的人了。不过这个我们也有,我们 Kotlin 程序员也可以不 new 就有对象了。
Kotlin 的构造函数分为主构造函数和次构造函数。主构造函数是和类名称一起写在类头部,次构造函数是写在类体里面的。它们都是用 constructor
修饰。不过在定义主构造函数时,如果没有注解什么的,就可以不写了,所以看起来还是很的。
class Person constructor(firstName: String) {
}
class Person(firstName: String)
Swift:
,和 Swift 的概念差不过,我们叫「指定初始化器」(Designated Initializer)和「便捷初始化器」(Convenience Initializer)。都是写在类里面的,指定初始化器用 init
修饰,便捷初始化器需要再加个 convenience
关键字。
class SomeClass {
private let string: String
init(string: String) { self.string = string
}
convenience init() { self.init(string: "KotlinThree")
} func printString() { print(string)
}
}
哎,你们这样主构造函数的初始化代码写在哪里。
Kotlin:
,这个问题问的太好了,我们主构造函数不能写代码。不过 Kotlin 提供了以 init 为关键字的初始化块用来写初始化代码,以解决主构造函数不能写代码的问题。
class Customer(name: String) {
init {
logger.info("Customer initialized with value ${name}")
}
}
Kotlin 的次构造函数必须直接或间接(通过其他次构造函数)委托给主构造函数,委托到同一个类的另一个构造函数用 this 关键字,你们应该也有这个限制吧。
Swift:
是的,Swift 便捷初始化器需要去调用指定初始化器来完成初始化。听说你们可以用构造函数的参数定义类的属性。
Kotlin:
哟,。Kotlin 可以通过在主构造函数参数前面添加 val 或者 var 修饰符,这样主构造函数的参数就变成了类的属性,这样就不需要再在类里面定义同样的属性再赋值了。
Swift:
666,。
Swift 有类方法和实例方法,用 class 或 static 关键字修饰的方法就是类方法,这两个关键字的区别是 class 修饰的类方法可以被子类复写,static 修饰的类方法不行。既没用 class 修饰,也没用 static 修饰的就是实例方法。
Kotlin:
Kotlin 里面已经没有类方法的概念了。不过,Kotlin 中可以用 object 关键字直接定义一个对象,在类内部,我们可以用 companion 为类声明一个伴生对象。伴生对象的成员可通过只使用类名作为限定符来调用,伴生对象的成员看起来像 Java 的静态成员,在运行时他们仍然是真实对象的实例成员。在 JVM 平台,如果使用 @JvmStatic 注解,你可以将伴生对象的成员生成为真正的静态方法和字段。
不过你们的类方法还可以被子类重写,这个在 Java 里也不行。诶,说说你们类是怎么继承的啊。
Swift
Swift 中用 :
来声明类的继承关系,你们也是用冒号来继承一个类吧?
Kotlin:
是的,再也不用区分 extends
还是 implements
了。在 Kotlin 里面,所有的非抽象类默认都是静态的,也就是相当于 Java 中的 final。如果想要让某个类可以被继承,必须要现式的为该类添加 open 的关键字,该关键字提供了和 Java 中 final 相反的功能。
Swift:
,为什么要区分?
Kotlin:
因为在 Java 继承类和实现接口使用不同的关键字。
Swift:
Swift 中继承类和实现协议也都是用同一个 :
符号。Swift 里面如果不想让一个类能被继承,可以在声明类时加上 final 关键字。另外如果两个类分辨属于不同的模块,基类必须用 open 关键字修饰才能被另一个模块的类继承。
Kotlin:
那你们有没有抽象类的概念啊。
Swift:
没有。
Kotlin:
Kotlin 不但类默认是静态的,函数也是静态的,如果一个函数需要被重写,我们必须手动让他变成开放的,即在函数前面添加 open 关键字。如果子类想要重写某个方法,必须用 override 关键字修饰该方法,否则会报错。被 override 修饰的函数默认也是开放的,如果不想它再被继承,需要 final 来修饰该函数。
Swift:
Swift 函数倒是不需要,不过也需要用 override 关键字来修饰。构造函数的覆盖也是一样的,子类覆盖父类初始化器的步骤:
初始化子类的所有成员变量
用 super 调用父类的初始化器
一些额外的操作
class SomeClass: BaseClass { let text: String
override init() {
text = "abc"
super.init()
loadData()
}
...
}
Kotlin:
Kotlin 如果有主构造函数的话,是直接在父类名称后面传递对应的参数。如果类没有主构造函数,那么每个次构造函数必须 使用 super 关键字初始化其基类型,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:
class Derived(p: Int) : Base(p)
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
Kotlin 属性覆盖和函数覆盖类似,需要使用 open 和 override 关键字,覆盖属性的类型必须兼容。可以用 var 属性覆盖 val 属性,反过来却不行。这是因为 var 本质上是多声明了一个 setter 方法。
Swift:
Swift 中储值属性不能覆盖,只能覆盖父类的计算属性,同样需要加上 override 关键字,不过属性覆盖用的不是很多。
Kotlin:
Swift 如果实现多个接口,会不会有不同协议带来同名函数的冲突的问题。
Swift:
Swift 如果有同样的名字 IDE 会报错,所以不同的协议如果被同一个类实现不能用同样的名字。
Kotlin:
Kotlin 可以,Kotlin 有一套规则来处理这样的冲突。在 Kotlin 中,如果一个类从它的直接超类继承相同成员的多个实现(由于接口函数可以有实现),它必须覆盖这个成员并提供其自己的实现。 为了表示采用从哪个超类型继承的实现,我们使用由尖括号中超类型名限定的 super,如 super。
open class A {
open fun f() { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } // interface members are ''open'' by default
fun b() { print("b") }
}
class C() : A(), B {
// The compiler requires f() to be overridden:
override fun f() {
super<A>.f() // call to A.f()
super<B>.f() // call to B.f()
}
}
Swift:
这个不错,可以不怕名字冲突了。给你们手动点个赞 。
Kotlin:
,Kotlin 里面还新增了嵌套类的概念,就是可以直接在类体里面另外一个类,其实就是之前 Java 里面的静态内部类。这种写法在 Java 里面就是定义内部类,在 Kotlin 里面要定义内部类反而要加上 Inner
关键字。
Swift:
Swift 没有内部类的概念。
Kotlin:
你们没有抽象类,也没有内部类,不过你们的协议好像有很多玩法。下次听你给我好好讲讲你们的协议是怎么样的?
Swift:
好的,没有问题。
技术知识
Kotlin
类的定义
Kotlin 使用 class
关键字声明类。
class A {
}
类声明由类名称、类头(指定其类型参数、主 构造函数等)和由大括号包围的类体构成。类头和类体都是可选的; 如果一个类没有类体,可以省略花括号。
class A
实例化
但我们想要实例化一个类的对象的时候,不需要提供 new
关键字。
val invoice = Invoice()
val customer = Customer("Joe Smith")
构造函数
Kotlin 的构造函数分为 主构造函数 和 次构造函数,主构造函数只能有一个,次构造函数可以有多个。
主构造函数
主构造函数是类头的一部分:它跟在类名(和可选的类型参数)后,用 constructor
关键字表示。
class Person constructor(firstName: String) {
}
当主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor
关键字。
class Person(firstName: String)
主构造函数没有自己的函数块,所以不能写任何代码,Kotlin 提供了以 init
为关键字的初始化块用来写初始化代码,以解决主构造函数不能写代码的问题,所以在初始化块中可以随意访问主构造函数的参数。
class Customer(name: String) {
init {
logger.info("Customer initialized with value ${name}")
}
}
默认情况下主构造函数的参数只能被 初始化块
和 属性初始化
访问。
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
不过我们可以通过在主构造函数参数前面添加 val
或者 var
修饰符,这样朱构造函数的参数就变成了类的属性。
class Person(val firstName: String, val lastName: String, var age: Int) {
// ……
}
PS: 如果构造函数有注解或可见性修饰符,这个 constructor 关键字是必需的,并且 这些修饰符在它前面
次构造函数
次构造函数也是用 constructor
修饰,写在类体里面,可以有多个。
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}
如果类有主构造函数的话,次构造函数必须直接或间接(通过其他次构造函数)委托给主构造函数,委托到同一个类的另一个构造函数用 this
关键字。
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的 不带参数的主构造函数。构造函数的可见性是 public。如果你不希望你的类 有一个公有构造函数,你需要声明一个带有非默认可见性的空的主构造函数。
class DontCreateMe private constructor () {
}
类的成员
Kotlin 类可以包含下面这些
构造函数和初始化块
函数
属性
嵌套类和内部类
对象声明
继承
我们用 :
声明要继承一个超级类,放在冒号后面。
open class Base(p: Int)
class Derived(p: Int) : Base(p)
如果类没有明确声明一个父类的话,默认是继承自 Any
,Any
并不是 java.lang.Object;它除了 equals ()、hashCode () 和 toString () 外没有任何成员。 更多细节请查阅 Java 互操作性部分。
在 Kotlin 里面,所有的非抽象类默认都是静态的,也就是相当于 Java 中的 final。如果想要让某个类可以被继承,必须要现式的为该类添加 open
的关键字,该关键字提供了和 Java 中 final 相反的功能。
如果类没有主构造函数,那么每个次构造函数必须 使用 super 关键字初始化其基类型,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
抽象类与接口
同 Java 一样,Kotlin 用 abstract
声明一个抽象类,用 interface
关键字来定义接口,与 Java8 相似,接口中可以有函数的实现。
与 Java 不同,你可以在接口中定义属性。在接口中声明的属性要么是抽象的,要么提供 访问器的实现。在接口中声明的属性不能有幕后字段(backing field),因此接口中声明的访问器 不能引用它们。
interface MyInterface {
val prop: Int // 抽象的
val propertyWithImplementation: String
get() = "foo"
fun foo() {
print(prop)
}
}
class Child : MyInterface {
override val prop: Int = 29
}
覆盖方法
默认类的函数也是静态的,如果一个函数需要被重写,我们必须手动让他变成开放的,即在函数前面添加 open
关键字。如果子类想要重写某个方法,必须用 override
关键字修饰该方法,否则会报错。
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
被 override
修饰的函数默认也是开放的,如果不想它再被继承,需要 final
来修饰该函数。
open class AnotherDerived() : Base() {
final override fun v() {}
}
覆盖属性
属性覆盖和函数覆盖类似,需要使用 open
和 override
关键字,覆盖属性的类型必须兼容。
open class Foo {
open val x: Int get { ... }
}
class Bar1 : Foo() {
override val x: Int = ...
}
我们可以用 var
属性覆盖 val
属性,反过来却不行。这是因为 var
本质上是多声明了一个 setter
方法。
此外,我们还可以在类的主构造函数的参数也可以同样实现属性覆盖。
interface Foo {
val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
override var count: Int = 0
}
覆盖规则
在 Kotlin 中,如果一个类从它的直接超类继承相同成员的多个实现(由于接口函数可以有实现),它必须覆盖这个成员并提供其自己的实现。 为了表示采用从哪个超类型继承的实现,我们使用由尖括号中超类型名限定的 super,如 super。
open class A {
open fun f() { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } // interface members are ''open'' by default
fun b() { print("b") }
}
class C() : A(), B {
// The compiler requires f() to be overridden:
override fun f() {
super<A>.f() // call to A.f()
super<B>.f() // call to B.f()
}
}
同时继承 A 和 B 没问题,并且 a () 和 b () 也没问题因为 C 只继承了每个函数的一个实现。 但是 f () 由 C 继承了两个实现,所以我们必须在 C 中覆盖 f () 并且提供我们自己的实现来消除歧义。
伴生对象
Kotlin 中可以用 object
关键字直接定义一个对象,在类内部,我们可以用 companion
为类声明一个伴生对象。伴生对象的成员可通过只使用类名作为限定符来调用,伴生对象的成员看起来像 Java 的静态成员,在运行时他们仍然是真实对象的实例成员。在 JVM 平台,如果使用 @JvmStatic 注解,你可以将伴生对象的成员生成为真正的 静态方法和字段。更详细信息请参见 Java 互操作性一节。
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
val instance = MyClass.create()
内部类和嵌套类
我们可以直接在一个类里面定义另外一个类。当里面的类被 Inner
修饰符修饰,则是类的内部类,否则我们只是定义了一个嵌套类。
嵌套类:
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo = Outer.Nested().foo() // == 2
内部类:
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo() // == 1
在 Kotlin 也有匿名内部类的概念,不过使用 object
表达式修饰:
window.addMouseListener(object: MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
})
Swift
类的定义
Swift 中用 class
关键字来定义类:
class SomeClass {
}
然后可以用以下方式创建这个类的实例:
let instance = SomeClass()
PS: Swift 中没有 NSObject 的基类概念
构造函数(初始化器)
构造函数也可以叫做初始化器(Initializer)
用 init
关键字来定义类的构造函数
class SomeClass { init(string: String) {
}
}let instance = SomeClass(string: "KotlinThree")
如果类含有成员变量,在类初始化时,必须保证所有成员变量都被初始化。
对于 Optional 类型的成员变量,如果没有显式地初始化,编译器会自动把它初始化为 nil。对于非 Optional 类型的成员变量,必须显式地初始化。
class SomeClass { let number: Int
let defaultValueNumber = 3
let optionalString: String? init() {
number = 1
}
}
析构函数
用 deinit
关键字来定义析构函数,析构函数在类销毁时调用。
class SomeClass { deinit {
}
}
指定初始化器和便捷初始化器
指定初始化器(Designated Initializer)是类的主要初始化器,每个类都至少需要有一个指定初始化器。
在上面的例子中用 init 定义的就是指定初始化器。
便捷初始化器(Convenience Initializer)需要用 convenience
来修饰。便捷初始化器需要去调用指定初始化器来完成初始化。
class SomeClass {
private let string: String
init(string: String) { self.string = string
}
convenience init() { self.init(string: "KotlinThree")
} func printString() { print(string)
}
}let instance = SomeClass()
instance.printString()/*
输出结果:
KotlinThree
*/
类方法和实例方法
用 class
或 static
关键字修饰的方法就是类方法,这两个关键字的区别是 class
修饰的类方法可以被子类复写,static
修饰的类方法不行。
既没用 class
修饰,也没用 static
修饰的就是实例方法。
class SomeClass { class func classMethod() {
} static func cannotBeOverridedClassMethod() {
} func instanceMethod() {
}
}// 调用类方法SomeClass.classMethod()
// 调用实例方法let instance = SomeClass()instance. instanceMethod()
类的继承
Swift 中用 :
来声明类的继承关系
class BaseClass { func baseFunction() {
}
}class SomeClass: BaseClass {
}
子类会获得父类的非 private 的属性和方法
let instance = SomeClass()
instance.baseFunction()
如果不想让一个类能被继承,可以在声明类时加上 final
关键字
final class BaseClass {}
另外如果两个类分辨属于不同的模块,基类必须用 open
关键字修饰才能被另一个模块的类继承。
PS: Swift3 提供了五个访问控制的关键字 open``public
private
fileprivate
internal
用来控制访问级别,internal
表示在模块内部可以完全访问,在模块外部不行,fileprivate
限定只能在当前的源文件中使用
必要初始化器
必要初始化器(Required Initializers)使用 required
来修饰表明,如果子类继承父类都必须实现该初始化器。如果不使用指定初始化器,则无需显示写出。
class BaseClass {
public var string: String
required init() {
string = "string"
}
}
class SomeClass: BaseClass {
required init() {} //如不实现则报错, 如不创建指定初始化器则可以省略
init(label: String) {
super.init()
}
}
方法覆盖
子类如果需要复写父类的方法,需要用 override
关键字来修饰
class SomeClass: BaseClass { override func baseFunction() { // ...
}
}
子类方法调用父类的相同方法,用 super
关键字,例如:
class SomeClass: BaseClass { override func baseFunction() { super.baseFunction() // ...
}
}
覆盖父类初始化器的步骤:
初始化子类的所有成员变量
用 super 调用父类的初始化器
一些额外的操作
class SomeClass: BaseClass { let text: String
override init() {
text = "abc"
super.init()
loadData()
}
}
类的嵌套
Swift 中可以在类中再定义一个类
class SomeClass {
class AnotherClass {
}
}
可以用 .
号的方式来在外面实例化里面嵌套的类
let instance = SomeClass.AnotherClass()
也可以给 AnotherClass 加上 private
,这样就无法再外面实例化了,只能在 SomeClass 这个类中实例化。
class SomeClass {
private class AnotherClass {
}
private let instance = AnotherClass() // 可以编译通过}let instance = SomeClass.AnotherClass() // 无法编译通过
关于《Swift vs. Kotlin 漫谈》系列
《Swift vs. Kotlin 漫谈》系列之变量定义
《Swift vs. Kotlin 漫谈》系列之函数定义
《Swift vs. Kotlin 漫谈》系列之控制流
《Swift vs. Kotlin 漫谈》系列之基本类型
《Swift vs. Kotlin 漫谈》系列是由 KotlinThree 发起的,旨在把 Swift 和 Kotlin 进行一个全面的对比,帮助 iOS 和 Android 开发对彼此的语言之间有一个更加深入的认识。如果你有兴趣可以关注我们公众号,也可以通过「原文链接」查看详情。
本文分享自微信公众号 - Android 群英传(android_heroes)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与 “OSC 源创计划”,欢迎正在阅读的你也加入,一起分享。
2022 Kotlin 中文开发者大会倒计时,迎接周末两天专属 Kotlin 的技术盛宴!
@所有 Kotlin 开发者们:
年度最值得期待的
Kotlin 开发者活动
就将于明天举行!
距离直播开始只有不到 24 小时
这次大会应该怎么看?
有哪些看点值得期待?
还有哪些信息是你需要知道的?
这些大家关注的问题,
我们统统在这篇推文做出解答。
第一天
11 月 26 日(周六)14:00 - 17:35
第二天
11 月 27 日(周日)14:00 - 17:35
本次活动将在 JetBrains 中国官方微信视频号和 BiliBili 频道同步直播。
点击下方按钮预约视频号直播
点此直通 BiliBili 直播间
如果你周末没空收看直播也不用担心,大会内容将全程录影,待剪辑后陆续公布回放视频。不过我们还是强烈建议大家收看直播,抓住每一个与嘉宾、观众提问交流(和抽奖)的机会~
向上滑动阅览首日大会安排
向上滑动阅览次日大会安排
如果你想要了解每场演讲内容更详细的介绍,可以点击下方图片,查看我们过往推送或进入大会官网页面探索。
嘉宾剧透之 AB 篇
关键词:Kotlin Symbol Processor
嘉宾剧透之王鹏篇
关键词:Compose Multiplatform
嘉宾剧透之 Kate 篇
关键词:LeetCode、刷题
嘉宾剧透之乔禹昂篇
关键词:Kotlin Multiplatform Mobile
除了技术主题外,这次活动还专门开辟了有关 Kotlin 学习历程及 Kotlin User Group 社区运营经验的专场分享。也让我们可以聆听 Kotlin 新鲜血液的声音。
嘉宾剧透之 KUG 伙伴篇
关键词:学习方法、社区运营
最最最宝贵的干货和分享
将在明天 14:00 之后的直播间等着你!
还没有预约的小伙伴
也请抓紧时间在下方完成一键预约。
我们期待和各位 Kotlin 开发者们一同
探索 Kotlin 的无限潜力,
展望 Kotlin 的无界未来。
祝大家都能在这次大会中有所收获!
我们明天见!
更多阅读推荐
新发布
Fleet 公共预览版
代码质量平台Qodana | Kotlin 1.7.0
JetBrains IDE 和 .NET 工具2022.2更新
Space On-Premises(本地部署版)Beta
调研报告
Python开发者年度调查 | Go语言现状调查
Kotlin Multiplatform使用现状
代码审查工具报告 | 2021开发者生态系统报告
IDE 使用技巧
IntelliJ IDEA代码注释 | 10个妙招解放鼠标
提高效率的5个GoLand快捷键
CLion调试器技巧 | 10大WebStorm快捷键
⏬ 戳「阅读原文」访问大会官网