scala类-单例对象-特质


git地址
https://github.com/a18792721831/studyScala.git

1.scala类

scala类和java类定义类似,格式、组成相同。

不同点是scala默认是public,而java默认是package(包内)
首先创建一个scala的类

class MyTest {
  var id: Long = _
  var name: String = _
  private var age: Int = _
  def getAge: Int = age
  def setAge(age: Int): Unit = this.age = age
  override def toString: String = "id:" + this.id +",name:" + this.name + ",age:" + this.age
  val gAge = () => println(this.age)
  val sAge = (age: Int) => {
    this.age = age
    age
  }
}

这个类很简单:
两个共有属性,一个私有属性,并且属性可变,有默认值。
为私有属性提供了读取、设置方法。
重写了toString方法。
还有两个函数,分别是打印函数和设置函数。

然后创建一个伴生对象

object MyTest{
  def main(args: Array[String]): Unit = {
    val myTest = new MyTest()
    println(myTest)
    myTest.id = 1
    myTest.name = "lili"
    myTest.age = 234
    println(myTest)
    println(myTest.getAge)
    myTest.setAge(0)
    println(myTest)
    println()
    println(myTest.gAge)
    println(myTest.sAge)
    myTest.gAge
    println(myTest.sAge(3))
    println(myTest.getAge)
  }
}

先别问为什么需要创建伴生对象,后面会研究到的。

先执行看看结果

id:0,name:null,age:0
id:1,name:lili,age:234
234
id:1,name:lili,age:0

com.study.MyTest$$Lambda$1/2093631819@1ff8b8f
com.study.MyTest$$Lambda$2/1828972342@387c703b
3
3

Process finished with exit code 0

可以看到,scala类的属性、方法和java基本一样。
scala的函数与java的lambda是一个。
接下来我们看下反编译java
在这里插入图片描述
有点意思,好像与我们的class和object能对应上。
这两个文件,用class查看软件只能看伴生对象的class文件
在这里插入图片描述
接下来用在线反编译等手段,看下scala类

import java.lang.invoke.SerializedLambda;
import scala.runtime.BoxesRunTime;
import scala.Predef$;
import scala.Function1;
import scala.runtime.BoxedUnit;
import scala.Function0;
import scala.reflect.ScalaSignature;

public class MyTest
{
    private long id;
    private String name;
    private int com$study$MyTest$$age;
    private final Function0<BoxedUnit> gAge;
    private final Function1<Object, Object> sAge;
    
    public static void main(final String[] args) {
        MyTest$.MODULE$.main(args);
    }
    
    public long id() {
        return this.id;
    }
    
    public void id_$eq(final long x$1) {
        this.id = x$1;
    }
    
    public String name() {
        return this.name;
    }
    
    public void name_$eq(final String x$1) {
        this.name = x$1;
    }
    
    private int com$study$MyTest$$age() {
        return this.com$study$MyTest$$age;
    }
    
    public void com$study$MyTest$$age_$eq(final int x$1) {
        this.com$study$MyTest$$age = x$1;
    }
    
    public int getAge() {
        return this.com$study$MyTest$$age();
    }
    
    public void setAge(final int age) {
        this.com$study$MyTest$$age_$eq(age);
    }
    
    @Override
    public String toString() {
        return new StringBuilder(14).append("id:").append(this.id()).append(",name:").append(this.name()).append(",age:").append(this.com$study$MyTest$$age()).toString();
    }
    
    public Function0<BoxedUnit> gAge() {
        return this.gAge;
    }
    
    public Function1<Object, Object> sAge() {
        return this.sAge;
    }
    
    public MyTest() {
        this.gAge = (Function0<BoxedUnit>)(() -> Predef$.MODULE$.println((Object)BoxesRunTime.boxToInteger($this.com$study$MyTest$$age())));
        this.sAge = (Function1<Object, Object>)(age -> {
            $this.com$study$MyTest$$age_$eq(age);
            return age;
        });
    }
}

可以看到,属性都被编译为了私有,同时生成了一些用到的方法
根据名字猜测:
在这里插入图片描述
这个main就是java中我们经常写的main
被代理到了内部类中了。内部类就是我们的伴生对象。
在这里插入图片描述
应该是提供直接访问的。
在Java中,我们的属性如果是公有或者保护或者默认,那么在同路径的类中是可以直接使用的。
在scala中默认是公有的。
所以可以直接用。
但是,实际上还是私有的,只不过生成了属性同名的访问方法。造成了我们对于私有属性可以直接使用的假像(对于无参方法,会自动填充小括弧)
在这里插入图片描述
应该是为了实现公有属性直接赋值。
在这里插入图片描述
toString方法用StringBuilder进行了优化。
在这里插入图片描述
函数是在构造时进行初始化的。

综上,原本期望使用_进行初始化属性,结果反编译没有体现出来。。。

1.1 scala类的方法调用

之前就知道,在scala中,如果没有参数,可以将方法的小括弧省略。
但是,还有一句,如果在定义时,方法就没有小括弧,那么在使用时,也不能加小括弧。
比如:

class MyApply {
  def apply() = println("apply class with ()")
  def apply1 = println("apply1 class without ()")
  def apply2() = println("apply2 class without ()")
}

我们创建了一个scala类,scala类有三个方法
apply()
apply1
apply2()
然后我们创建一个object去调用

object MyTestApply {

  def main(args: Array[String]): Unit = {
    val myApply = new MyApply
    println(myApply)
    myApply()
    myApply.apply1
    myApply.apply2
    myApply.apply2()
  }

}

object也非常的简单,首先创建了一个不可变对象,然后打印了对象,然后分别调用这三个方法。
输出如下:

com.study.MyApply@2be94b0f
apply class with ()
apply1 class without ()
apply2 class without ()
apply2 class without ()

Process finished with exit code 0

接下来看看Java代码
这是scala类的
在这里插入图片描述
这是object对应的类的
在这里插入图片描述
这是object对应的内部类
在这里插入图片描述

1.2 scala类的apply方法

在调用scala类的apply方法时,可以不显示的调用,而是使用变量名加括弧调用。
注意,这样方式只适用于有小括弧的apply方法。

2. scala Object 单例对象

单例对象用于持有一个类的唯一实例。
为什么?
看看1中的例子。
我们创建了一个scala类,一个单例对象。
反编译结果是两个类,一个内部类。
首先scala类对应了反编译Java类,而单例对象生成了反编译Java类和内部类。
单例对象的反编译Java类作为执行入口,将实际的操作转到内部类的main方法
在这里插入图片描述
在这里插入图片描述
在内部类中定义了静态公有不可继承的MODULE$对象。反编译Java类通过这个对象调用实际的内部类的main方法。

所以,可以这么认为,因为内部类中的MODULE$对象是实际操作入口,也是静态的,直接new的。所以object内部类持有了object的唯一入口,也即唯一实例。

MD,有点绕。后面慢慢理解吧。
反正就这样用。

2.1 单例对象的apply

在单例对象中也可以实现scala类中apply方法,直接让变量+()调用apply方法。
在单例对象中也可以实现。不过,因为单例对象是单例的,所以可以直接Object名字+()调用单例对象的apply
举例如下:
首先创建一个object:

object TestApply {

  def apply() = println("TestApply")

}

调用

object TestMain{
  def main(args: Array[String]): Unit = {
    TestApply()
  }
}

运行结果

TestApply

Process finished with exit code 0

看下反编译结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3. Trait 特质

怎么说呢,这玩意和Java接口应该是一个东西(jdk1.8之后的接口,可以写默认方法)
创建一个特质
在这里插入图片描述
class实现
在这里插入图片描述
class继承
在这里插入图片描述
object实现
在这里插入图片描述
内部匿名实现
在这里插入图片描述
测试
在这里插入图片描述
所以说,Java中怎么用接口,在scala中就怎么用trait
不过,scala貌似不支持上下转型。

3.1 实现多个特质

我们在上面的基础上在创建一个特质
在这里插入图片描述
然后创建scala类实现
在这里插入图片描述
创建object调用
在这里插入图片描述
执行结果
在这里插入图片描述
这玩意和接口完全相同。
看下反编译代码
在这里插入图片描述
但是多多少少还是和没有实现特质的scala类有一定的区别。
没有实现特质的scala类的反编译代码实际所有的操作都在内部类(应该不叫内部类…不过都是用$进行区分,一个在钱,一个在后),但是实现了特质的scala类的实现都在外部类

3.2 new对象时实现特质

scala比较骚的一点是可以在new对象时实现特质。
Java貌似不行。
这就使得scala非常的灵活。
举个例子:
首先创建一个普通的类
在这里插入图片描述
在new对象时,实现特质
在这里插入图片描述
在这里插入图片描述
看看反编译代码
在这里插入图片描述
这个应该是继承原来的scala类,实现了特质的一个内部类
在这里插入图片描述
表现为new后面实现了方法(内部类)
还有就是类名前面有$标识等等。

3.3 函数实现特质

在scala中,主要是对象的函数式编程。
在某些资料中,函数是这样被定义的:
函数是一些特质实现的集合。
所以函数的本质是类的实例。
举个例子
首先看看函数的特质
在这里插入图片描述
接下来实现它
在这里插入图片描述
然后进行使用
在这里插入图片描述
输出
在这里插入图片描述
在这里插入图片描述
当然,还有快捷的写法

在这里插入图片描述
使用 =>语法糖
看下反编译代码
在这里插入图片描述

3.4 传名参数

传名参数是指类似如下的定义
在这里插入图片描述
表示方法test的参数是一个函数,=>语法糖,而函数的执行结果是String,然后test对函数的执行结果的String进行操作。

举例:
在这里插入图片描述
这里存在将方法自动展开,即EAT展开。
如果在需要函数的地方传入了方法,自动将方法转为函数。
在这里插入图片描述
在这里插入图片描述

发布了182 篇原创文章 · 获赞 88 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/a18792721831/article/details/103243552