工作中偶然发现Scala构造方法中的参数,无论是否有val/var修饰都可以顺利编译运行,如下:

     class AA(name: String)
class BB(val name: String)

那么两者的区别在哪里呢?对于case class呢?其区别又在哪里?其应用场景又在哪里呢?下面就辨析一下如下几个类的区别

     class AA(name: String)
class BB(val name: String)
class CC(var name: String)
class DD(private val name: String)
class EE(private[this] val name: String)
case class FF(name: String)
case class GG(val name: String)
case class HH(var name: String)
case class II(private val name: String)
case class JJ(private[this] val name: String)

单纯的从代码中来看,发现不了什么区别,只是简单的多了一个val的修饰符。为了一探究竟,先对源码进行编译,然后通过javap对其class文件进行反编译,查看其与源码的区别。

一、普通类构造器中val/var 存在和不存在的区别

源码:

     class AA(name: String)
class BB(val name: String)
class CC(var name: String)

反编译结果:

 Compiled from "Test.scala"
public class AA {
public AA(java.lang.String);
} Compiled from "Test.scala"
public class BB {
private final java.lang.String name;
public java.lang.String name();
public BB(java.lang.String);
} Compiled from "Test.scala"
public class CC {
private java.lang.String name;
public java.lang.String name();
public void name_$eq(java.lang.String);
public CC(java.lang.String);
}

结论:构造器中val修饰的参数,编译之后会在该类中添加一个private final的全局常量,同时提供一个用于获取该常量的public方法,无设置方法。

   构造器中var修饰的参数,编译之后会在该类中添加一个private的全局变量,同时提供两个获取和设置该变量值的public方法。

   构造器中无修饰符的参数,则该参数属于构造函数内的局部参数,仅仅在该构造函数内部访问。

二、普通类构造器中private和private[this] 修饰参数的区别

源码:

     class DD(private val name: String)
class EE(private[this] val name: String)

反编译结果:

Compiled from "Test.scala"
public class DD {
private final java.lang.String name;
private java.lang.String name();
public DD(java.lang.String);
} Compiled from "Test.scala"
public class EE {
public EE(java.lang.String);
}

结论:private 修饰的构造器参数,编译之后会在该类中添加一个private final的全局常量,同时提供一个private的访问该参数的方法。即该参数可在该类范围内访问。

   private[this]修饰的构造器参数,不存在全局变量,只能在该构造方法中访问,在该类中无法访问。同 class AA(name: String)

注意:Scala整个类体就是其构造函数,所以,站在Scala角度看,private[this]修饰的构造器参数能够在整个类中访问,而站在Java角度看,该参数仅仅能够在构造函数中访问,在类中无法访问。而站在Scala角度看,private[this]和 private的主要区别在于,private[this]修饰的参数无法通过e.name的方式访问,即使在该类的内部。注意下图:

图中,在EE#show中是无法访问that.name的,即使that的类型本身就是EE也不行的。这才是private[this]和private在Scala中的主要区别。

三、普通class和case class的区别

源码:

   case class FF(name: String)

编译之后会发现在文件夹下面多出两个class文件,一个为FF.class,另一个为FF$.class文件。对两个文件进行反编译

反编译结果:

 Compiled from "Test.scala"
public class FF implements scala.Product,scala.Serializable {
//private final 修饰的name常量
private final java.lang.String name;
//public修饰获取name的方法,可用于外部访问
public java.lang.String name();
//public修饰的构造函数
public FF(java.lang.String);
public static scala.Option<java.lang.String> unapply(FF);
public static FF apply(java.lang.String);
public static <A> scala.Function1<java.lang.String, A> andThen(scala.Function1<FF, A>);
public static <A> scala.Function1<A, FF> compose(scala.Function1<A, java.lang.String>);
public FF copy(java.lang.String);
public java.lang.String copy$default$1();
public java.lang.String productPrefix();
public int productArity();
public java.lang.Object productElement(int);
public scala.collection.Iterator<java.lang.Object> productIterator();
public boolean canEqual(java.lang.Object);
public int hashCode();
public java.lang.String toString();
public boolean equals(java.lang.Object);
} Compiled from "Test.scala"
public final class FF$ extends scala.runtime.AbstractFunction1<java.lang.String, FF> implements scala.Serializable {
//静态的FF$对象
public static FF$ MODULE$;
//构造函数为private
private FF$();

//返回FF对象的 apply方法
public FF apply(java.lang.String);

public static {};
public final java.lang.String toString();
public scala.Option<java.lang.String> unapply(FF);
private java.lang.Object readResolve();
public java.lang.Object apply(java.lang.Object);
}

分析:

  先看FF.class

   1、对比class AA(name: String)的结果来看,case class 自动实现了scala.Product,scala.Serializable两个特质(接口),因此,case class类中自然也会实现这两个特质中的抽象方法/覆写一些方法(11~22行)。

 public class AA
public class FF implements scala.Product,scala.Serializable

   2、对比 public class BB,在类内部都具有一个全局常量name和一个获取该常量的public方法,同时两者都具有一个public的构造函数。

   3、实现了一些特质中的一些方法,覆写了Java Object中的一些方法。例如:andThen() toString() hashCode()等

  再看FF$

    1、有一个public  static修饰名为 MODULE$ 的 FF$对象

    2、构造器被私有化,用private修饰。

    3、组合1、 2、两点可知,FF$是一个单例类。其矢志不渝就是Scala中FF类的伴生对象

结论

  Scala编译器在对case class进行编译的时候做了特殊处理,扩展了其方法和功能,加入了scala.Product,scala.Serializable的特性,同时为其提供了该类的伴生对象。另外,对于case class 构造器参数,其默认以public修饰,可允许外部调用。

四、其他

上面几个对比理解了,下面这几个类之间的区别也就可以举一反三了。

     case class GG(val name: String)
case class HH(var name: String)
case class II(private val name: String)
case class JJ(private[this] val name: String)

=========================================

原文链接:case class 和class的区别以及构造器参数辨析 转载请注明出处!

=========================================

-----end

case class 和class的区别以及构造器参数辨析的更多相关文章

  1. strong,weak, retain, assign的区别@property的参数

    strong,weak, retain, assign的区别@property的参数 先说经验 使用场合 copy:NSString,block, weak:UI控件,代理 strong:一般对象.自 ...

  2. effective java 3th item2:考虑 builder 模式,当构造器参数过多的时候

    yiaz 读书笔记,翻译于 effective java 3th 英文版,可能有些地方有错误.欢迎指正. 静态工厂方法和构造器都有一个限制:当有许多参数的时候,它们不能很好的扩展. 比如试想下如下场景 ...

  3. Spring Boot 构造器参数绑定,越来越强大了!

    在之前的文章:Spring Boot读取配置的几种方式,我介绍到 Spring Boot 中基于 Java Bean 的参数绑定,在一个 Java Bean 类上用 @ConfigurationPro ...

  4. Java构建器(多个构造器参数)

    今天看netty权威指南,第一次听说构建器,百度了几个博客,但是并没有通俗易懂一点儿的,综合别人的博客,总结如下: 1. 构建器是什么? 当创建对象需要传入多个参数的时候我们通常会根据参数的数量写不同 ...

  5. if else if,switch case二者的联系与区别

    前段时间在学习中听到了一个关于条件判断语句的问题,分析if else if语句和switch case语句这两者之间的联系和区别,从而使用其中最有效率的一种方法. 一.if...else if if. ...

  6. 重载和重写的区别?构造器 Contructor 构造器是否可被 override?

    重载 发生在同一类,方法名必须相同,参数类型不同,顺序不同,类型不同,方法返回值和返回类型可以不同 重写 发生在子父类,方法名.参数名参数列表必须相同.返回值范围小于等于父类,抛出异常范围小于等于父类 ...

  7. android 点滴记录 ICCID IMSI IMEI MEID 关系 和 区别,相关参数在什么情况下可以获取...

    1:ICCID:Integrate circuit card identity 集成电路卡识别码(固化在手机SIM卡中) ICCID为IC卡的唯一识别号码,共有20位数字组成,其编码格式为:XXXXX ...

  8. Scala主构造器参数是否升级为成员与是否有get/set

    1:主构造器前面添加val/var 关键字则升级为类成员,否则只是构造器中的一个参数而已. 2:private 修饰get/set方法权限,private var/val 成员变量,则有get/set ...

  9. 链式编程:遇到多个构造器参数(Constructor Parameters)时要考虑用构建器(Builder)

    public class NutritionFacts { private final int servingSize; private final int servings; private fin ...

随机推荐

  1. golang struct

    ex1 /* https://golangbot.com/structs/ struct 结构 结构就是一组字段. */ package main import "fmt" // ...

  2. JDBC 链接mysql 8 的问题

    转载:jdbc连接mysql 8 的一些坑 1.驱动包要升级为 mysql-connector-java-8.0.11.jar 2.JDBC driver 由“com.mysql.jdbc.Drive ...

  3. [LeetCode&Python] Problem 703. Kth Largest Element in a Stream

    Design a class to find the kth largest element in a stream. Note that it is the kth largest element ...

  4. 通用Mapper的各个方法描述,参考官方

    下面是通用Mapper的各个方法描述,主要还是看官方的描述https://mapperhelper.github.io/all/. 基础接口 Select 接口:SelectMapper<T&g ...

  5. 02MYSQL查询语句

    查询语句是用于将表里的数据查询出来==查询可以返回一个结果集(表) | 或者的意思   * 代表当前表的所有字段 **查询语句的语法:select *| 字段名列表 from 表名 [where 条件 ...

  6. python自动化框架(一)

    一.jsonpath难点分析 dic = { "error_code": 0, "stu_info": [ { "id": 2057, &q ...

  7. H5中input[type="date"]默认样式修改 伪类

  8. kickstart文件制作与光盘镜像制作

    kickstart文件,是linux(Redhat.Centos.Fedora)下的anaconda安装程序的配置文件,基于此文件,可以实现linux的无人值守安装,在需要大规模部署安装linux的情 ...

  9. PYTHON之路,线程

    关于多任务的理解, 代码要执行,首先得变成机器认识的东西执行,那么需要解释器.那么执行按道理,一段程序在这里就具体来说一段代码的执行吧,我们知道代码的执行是从上至下按顺序执行,那么这里有条件分支结构, ...

  10. vue使用axios 时 后台接收不到数据

    后台用django 时,默认接收的数据格式为formdata ,前端如果传了其他格式会出现后台收不到参数的情况. 前端参数转 fromdata 代码如下 let formData = new Form ...