Kotlin开发笔记:类的创建,单例和匿名对象
*在次构造函数中,我们是不允许用val或者var修饰符来修饰传入的参数的,也就是说,在次构造函数中,我们不允许自动创建字段。这段代码先定义了一个成员变量address并先将其初始化为"",紧跟在后面的是它的set方法,定义一个变量的set或者get方法的格式就是紧跟在成员变量之后,用set代码块或者get代码块。在drawCircle方法中,我们通过object关键字生成了一个匿名类,该匿名类拥有
Kotlin开发笔记:类的创建,单例和匿名对象
访问修饰符
有四种修饰符:
- 1.public 所有均可访问
- 2.private 仅当前类中可访问
- 3.protected 允许派生类的方法访问
- 4.internal 允许同一个模块的代码访问
创建类
Kotlin中的类和Java不太一样,下面是一个简单的示例代码,我们通过这个示例代码学习Kotlin中的类的基本内容。
class Person (val name:String,val age:Int) {
//为address字段设置set方法
var address:String = ""
set(value) {
if (value == ""){
field = "Kotlin之父"
}else{
field = value
}
}
constructor(name:String,age:Int,address:String):this(name,age){
this.address = address
}
public var initNumber = 0
init {
initNumber = 100
println("假人已创建")
}
}
这是我们的Person类,猜一猜它有几个字段,正确答案是4个字段,或者更加通俗的来说,这个类里有四个成员变量,分别为:
- val name:String
- val age:Int
- var address:String
- var initNumber:Int
我们会发现,其中的name和age字段我们在类中并没有声明,name和age字段的创建是在第一行:
class Person (val name:String,val age:Int)
中的,其实这种写法是省略的写法,真正完整的写法应该是:
class Person constructor(val name:String,val age:Int)
类名后面跟的constructor是Person的主构造函数,一个类可以有许多构造方法,但是它们都要直接或者间接地调用主构造方法。
当我们在主构造函数中用val或者var声明变量时,就会在类中自动创建字段;如果不用val或者var声明,比如说 name:String 那么将不会自动生成字段。且在默认情况下,对类及其成员的访问是public,构造函数也是public,当主构造函数是public时,就可以将constructor关键字给省略。在Kotlin中,定义类的行实际上定义了主构造函数。
次构造函数
说完了主构造函数,接下来就看次构造函数。**在次构造函数中,我们是不允许用val或者var修饰符来修饰传入的参数的,也就是说,在次构造函数中,我们不允许自动创建字段。**在上述示例中,我们的次构造方法是:
constructor(name:String,age:Int,address:String):this(name,age){
this.address = address
}
次构造函数需要调用主构造函数或者其他次构造函数之一,被调用的构造函数在后面用冒号连接起来,上段代码中的:this(name,age)表明的就是调用了主构造函数,有点类似与子类需要调用父类的构造函数一样。
成员变量的set和get
接下来让我们看这部分:
var address:String = ""
set(value) {
if (value == ""){
field = "Kotlin之父"
}else{
field = value
}
}
这段代码先定义了一个成员变量address并先将其初始化为"",紧跟在后面的是它的set方法,定义一个变量的set或者get方法的格式就是紧跟在成员变量之后,用set代码块或者get代码块
var 变量名:类型 = 值
set(value){
....
field = value
}
get() = 变量名
其中set代码块中的value 和 field 是两个关键字,分别代表set方法传入的值 和 实际的成员变量,get代码块很好懂,直接返回成员变量的值即可。
如果类要实现接口或者继承类后面用冒号连接即可,比如:
class Person constructor(val name:String,val age:Int):Callable<String>,Runnable
初始化代码块
主构造函数声明是第一行的一部分。参数和属性在构造函数的参数列表中定义。不通过构造函数参数传递的属性也可以在类中定义。如果初始化对象的代码比只设置值更复杂,那么我们可以通过初始化代码块实现:
init {
initNumber = 100
println("假人已创建")
}
不过这里我们的逻辑并没有多复杂。一个类可以有0个或者多个init块。这些块作为主构造函数执行的一部分来执行。init块的执行顺序是自上而下的。在init块中,只能访问已经在块上面定义的属性。
底层的字段和属性
先给段代码:
println("创建类")
val FakePerson = Person("FakeMan",0,"Moon")
println("${FakePerson.name},${FakePerson.age},${FakePerson.address},${FakePerson.initNumber}")
println("\n")
这里我们看似是通过了FakePerson.name方法直接访问了name字段,但是实际上Kotlin在底层还是通过name的get方法来获取的,一切获取值或者设置值在底层都是通过set和get方法实现的。Kotlin中并不公开类的字段。
匿名类
和Java一样。Kotlin中也有匿名类,匿名类的定义是通过object关键字实现的:
fun drawCirCle() {
val circle = object {
val x = 10
val y = 20
val radius = 30
}
println("${circle.x},${circle.y},${circle.radius}")
}
在drawCircle方法中,我们通过object关键字生成了一个匿名类,该匿名类拥有x,y,radius三个成员变量,在这个方法中可以访问匿名类的字段。
匿名类产生的匿名对象有下面几个限制:
- 匿名对象的内部类型不能作为函数或者方法的返回类型。
- 匿名对象的内部类型不能用作函数或方法的参数类型
- 如果他们作为属性存储在类中,他们将视为Any类型,他们的任何属性或方法都将无法直接访问。
如果匿名类想要实现接口,只要在object关键字后面用冒号连接要实现的接口即可。
fun createTwoInterface():Runnable {
val twoInterface = object :Runnable,AutoCloseable{
override fun run() {
println("Implement run")
}
override fun close() {
println("Implement close")
}
}
return twoInterface
}
带有对象声明的单例
如果在object关键字和块{}之间放置一个名称,那么Kotlin将认为定义是语句或者声明而不是表达式。使用一个对象表达式声明来穿件一个单例:
object Util{
fun numberOfProcessors() = println(Runtime.getRuntime().availableProcessors())
}
这样就相当于声明了一个Util单例。
单例也并不局限于拥有方法,他也可以拥有属性和实现接口:
object Sun : Runnable{
val radiusInKM = 696000
var coreTemperatureInC = 1500000
override fun run() {
println("Sun's Run")
}
}
fun moveIt(runnable:Runnable){
runnable.run()
}
该单例实现了Runnable接口并且拥有自己的成员变量。
顶层函数,包和单例
如果我们想要实现一个类似于工具类的类,我们可以结合使用Package,单例和顶层函数:
package PackageTest
fun unitsSupported() = listOf("Mertic","Imperial")
fun precision(): Int = throw java.lang.RuntimeException("Error!!!")
//单例
object Tempterature {
fun c2f(c:Double) = c * 9.0/5 +32
fun f2c(f:Double) = (f - 32) * 5.0/9
}
object Distance{
fun milesToKm(miles:Double) = miles * 1.609
fun kmToMiles(kms:Double) = kms / 1.609
}
下面就是一个工具类的实现,我们用单例和包将它的方法组织起来,让它具有更好的结构并且可以像工具类一样使用它。总结下来就是:
- 如果一些函数是高级的,通用的和广泛使用的,那么就把它们放在顶级函数中。
- 如果一些函数之间的关系比其他的函数更加密切,就把它们放在一个单例中。
- 如果一些函数存在依赖关系,可以把它们放在一个单例中。
在使用时,我们就可以导包来使用。
完整演示代码
import PackageTest.*
import java.util.concurrent.Callable
fun main(){
//1.匿名类
println("匿名类")
drawCirCle()
println("\n")
//2.匿名类实现接口
println("匿名类实现接口")
createRuna().run()
println("\n")
//3.object 单例
println("单例")
Util.numberOfProcessors()
println("\n")
//4.单例实现接口
println("单例实现接口")
moveIt(Sun)
println(Sun.radiusInKM)
println("\n")
//5.Package + 单例 + 方法
println("Package + 单例 + 方法")
println(PackageTest.unitsSupported())
println(PackageTest.Tempterature.f2c(75.253))
println(Tempterature.c2f(24.305))
println("\n")
//6.创建类
println("创建类")
val FakePerson = Person("FakeMan",0,"Moon")
println("${FakePerson.name},${FakePerson.age},${FakePerson.address},${FakePerson.initNumber}")
println("\n")
}
//使用匿名内部类--也只能在该方法里访问
fun drawCirCle() {
val circle = object {
val x = 10
val y = 20
val radius = 30
}
println("${circle.x},${circle.y},${circle.radius}")
}
//方法返回一个Runnable 其中的匿名类实现了 Runnable接口
fun createRuna():Runnable {
var runn = object : Runnable{
override fun run() {
println("Execute--Runnable")
}
}
return runn;
}
//实现两个接口的匿名类
fun createTwoInterface():Runnable {
val twoInterface = object :Runnable,AutoCloseable{
override fun run() {
println("Implement run")
}
override fun close() {
println("Implement close")
}
}
return twoInterface
}
//简单实现接口
fun createSimpleInterface():Runnable = Runnable { println("Simple Implement Run Method") }
//单例对象
object Util{
fun numberOfProcessors() = println(Runtime.getRuntime().availableProcessors())
}
//单例拥有属性字段和方法
object Sun : Runnable{
val radiusInKM = 696000
var coreTemperatureInC = 1500000
override fun run() {
println("Sun's Run")
}
}
fun moveIt(runnable:Runnable){
runnable.run()
}
//创建类
class Person constructor(val name:String,val age:Int):Callable<String>,Runnable {
//为address字段设置set方法
var address:String = ""
set(value) {
if (value == ""){
field = "Kotlin之父"
}else{
field = value
}
}
get() = address
constructor(name:String,age:Int,address:String):this(name,age){
this.address = address
}
public var initNumber = 0
init {
initNumber = 100
if(name == ""){
initNumber = 99
}
println("假人已创建")
}
override fun call(): String {
TODO("Not yet implemented")
}
override fun run() {
TODO("Not yet implemented")
}
}
/** package中的演示 **/
package PackageTest
fun unitsSupported() = listOf("Mertic","Imperial")
fun precision(): Int = throw java.lang.RuntimeException("Error!!!")
//单例
object Tempterature {
fun c2f(c:Double) = c * 9.0/5 +32
fun f2c(f:Double) = (f - 32) * 5.0/9
}
object Distance{
fun milesToKm(miles:Double) = miles * 1.609
fun kmToMiles(kms:Double) = kms / 1.609
}
//总结下来:如果一些函数是高级的,通用的和广泛使用的,那么就把它们放在顶级函数中。
//如果一些函数之间的关系比其他的函数更加密切,就把它们放在一个单例中
//如果一些函数存在依赖关系,可以把它们放在一个单例中
更多推荐



所有评论(0)