Java要使用一个用完就会回收的对象,就要用到匿名对象。
Kotlin也会有匿名对象,Kotlin用的是对象表达式,这比Java的先进的多。
有时候,我们需要创建⼀个对某个类做了轻微改动的类的对象,而不用为之显示声明新的⼦类。Java ⽤匿名内部类 处理这种情况。Kotlin ⽤对象表达式和对象声明对这个概念稍微概括了下。
要创建⼀个继承⾃某个(或某些)类型的匿名类的对象,我们会这么写:
open class Sum<T:Number>{
open fun add(array:Array<T>):T{
var sum = 0.0
for (items in array){
sum = sum.toDouble() + sum.toDouble()
}
return sum as T
}
}
fun getSum(sum:Sum<Int>){ //这个方法传入了一个Sum类型的对象
var array = arrayOf(1,2,45,67)
var s = sum.add(array)
println(s)
}
fun main(args: Array<String>) {
getSum(object :Sum<Int>(){ //这里对原本的类改动,传入一个匿名对象
override fun add(array: Array<Int>): Int {
return super.add(array)
}
})
}
匿名对象也能像下面这么写:
open class Rest(){
open fun hello(){
println("hello")
}
}
fun main(args: Array) {
var rest = object : Rest(){
override fun hello(){
println("hello1")
}
}
rest.hello()
}
如果超类型有⼀个构造函数,则必须传递适当的构造函数参数给它。多个超类型可以由跟在冒号后⾯的逗号分隔的列表指定
其实就是继承某个类的时候要把超类初始化。类与对象中已经说明了
open class Base(var a:Int){
open fun say(){
println(a)
}
}
interface B{
fun hello()
}
fun main(args: Array) {
var sub = object : Base(1),B{
override fun say() {
super.say()
println("sub a")
}
override fun hello() {
println("hello")
}
}
sub.say()
sub.hello()
}
任何时候,如果我们只需要“⼀个对象⽽已”,并不需要特殊超类型,那么我们可以简单地写。(仅仅要一个对象,不需继承自任何类的时候)
fun main(args: Array) {
var o = object {
val a:Int = 1
val b:Int = 2
fun sum(){
println(a+b)
}
}
o.sum()
}
请注意,匿名对象可以⽤作只在本地和私有作⽤域中声明的类型。如果你使⽤匿名对象作为公有函数的返回类型或者⽤作公有属性的类型,那么该函数或属性的实际类型会是匿名对象声明的超类型,如果你没有声明任何超类型,就会是 Any 。在匿名对象中添加的成员将⽆法访问。
open class B1{
open val s = "hello"
}
class C {
//这里是私有的方法,所以返回的是匿名对象的类型
private fun privateFoo()= object {
val x = "jian"
}
//这里是公有方法,所以返回的是Any
fun publicFoo() = object{
val s = "dan"
}
//共有函数返回的对象要继承一个超类,覆盖超类的一些属性或者方法是可以的
fun publicFoo2() = object : B1{
override val s = "hi"
}
fun test(){
val x1 = privateFoo().x //编译能通过
val x2 = publicFoo().s //编译不能通过,因为返回的是Any,是访问不到s的
val x3 = publicFoo2().s //编译能通过,返回的是B1类型,其实这就是匿名内部类
}
}
就像 Java 匿名内部类⼀样,对象表达式中的代码可以访问来⾃包含它的作⽤域的变量。(与 Java 不同的是,这不仅限于 final 变量)。
open class A {
}
class B2 {
fun countClick():Int{
var clickCount = 0
var count = 0
var ob = object : A(){
fun addclickCount():Int{
clickCount++ //Java中如果要访问这个变量,这个变量在外部声明必须是final的,
//但是在kotlin中是不是final都一样
return clickCount
}
}
return ob.addclickCount()
}
}
fun main(args: Array) {
println(B2().countClick())
}
单例模式是⼀种⾮常有⽤的模式,⽽ Kotlin(继 Scala 之后)使单例声明变得很容易:
kotlin实现单例模式的方式:
//恶汉式
class AA private constructor(){
var i = 1
fun method(){
i++
println(i)
}
companion object {
@JvmStatic
val instance :AA = AA()
}
}
fun main(args: Array<String>) {
val a1 = AA.instance
val a2 = AA.instance
a1.method() //输出2
a2.method() //输出2
println(a1==a2) //输出ture
}
//懒加载
class AA1 private constructor(){
var i = 1
fun method(){
i++
println(i)
}
companion object {
@JvmStatic
val instance :AA1 by lazy { AA1()}
}
}
fun main(args: Array<String>) {
val a1 = AA1.instance
val a2 = AA1.instance
a1.method()
a2.method()
println(a1 == a2)
}
//使用枚举实现的单例模式
enum class A {
INSTANCE;
var i = 0
fun method(){
this.i++
}
}
fun main(args: Array<String>) {
var a = A.INSTANCE
a.method()
println(a.i) //输出1
var a1 = A.INSTANCE
a1.method()
println(a.i) //输出2
println(a==a1) //输出true
}
//超级简单的单例模式,直接声明一个有名字的对象,这个叫做对象声明
//这里要和匿名内部类区别开,匿名内部类是在方法中,对象声明是在全局
//声明的对象还能有超类(实现接口,或者继承某个类)
interface AA1
object AA:AA1{
var i = 0
fun method(){
i++
println(i)
}
}
fun main(args: Array) {
val a1 = AA
val a2 = AA
a1.method()
a2.method()
println(a1 == a2)
AA.method()//直接用声明的对象的名称,也能调用对象内部可见的成员
}
上面最后一种实现方式就是对象声明。并且它总是在 object 关键字后跟⼀个名称。就像变量声明⼀样,对象声明不是⼀个表达式,不能⽤在赋值语句的右边。(这种对象声明能出现在顶层,但是不能出现在“=”右边)
要引⽤该对象,我们直接使⽤其名称即可(上面代码已经做出示范)
注意:对象声明不能在局部作⽤域(即直接嵌套在函数内部),但是它们可以嵌套到其他对象声明或⾮内部类中。
这里要和匿名对象区分,匿名对象能出现在局部作用域。
上面的单例可以知道,我们实现单例模式除了使用对象声明来实现,还能使用伴生对像实现
访问伴生对象中属性、字段和方法,像java中访问静态成员一样,直接使用类名来访问
而且伴生对象的方法可以和实例方法同名
class BB {
var i = 1
fun method() {
i+ j
println(i+j)
}
//一个类里只能有一个伴生对象
companion object Test{
var j = 1
fun method(){
//伴生对象不能访问类的实例对象,只能访问伴生对象的属性
j++
println(j)
}
}
}
fun main(args: Array<String>) {
BB.method() //访问伴生对象的共有属性,能像Java访问静态成员一样的方式访问
BB().method()
BB.method()
}
请注意,即使伴生对象的成员看起来像其他语言的静态成员,在运行时他们仍然是真实对象的实例成员,而且,例如还可以实现接口
interface I{
fun test()
}
class BB1{
companion object Test:I{
override fun test() {
println("伴生对象实现接口")
}
}
}
当然,在 JVM 平台,如果使⽤ @JvmStatic 注解,你可以将伴生对象的成员生成为真正的静态方法和字段。更详细信息请参见Java 互操作性⼀节 。
如果要在Java代码中调用就要使用@JvmStatic
对象表达式和对象声明之间有⼀个重要的语义差别:
1. 对象表达式是在使⽤他们的地方立即执行(及初始化)的
2. 对象声明是在第⼀次被访问到时延迟初始化的
3. 伴生对象的初始化是在相应的类被加载(解析)时,与 Java 静态初始化器的语义相匹配