工作中偶然发现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. SharePoint 2013 新特性 (三) 破改式 —— 设计管理器的使用 [1.设备通道]

    首先,哥们儿们会问,为啥要有设计管理器呢,不是原来就可以编辑页面了么,原来那个编辑不了模板页和布局页,也不能打包,而且也看不到具体HTML代码,不能编辑CSS,当然,你安装的SharePoint De ...

  2. ifeve.com 南方《JVM 性能调优实战之:使用阿里开源工具 TProfiler 在海量业务代码中精确定位性能代码》

    https://blog.csdn.net/defonds/article/details/52598018 多次拉取 JStack,发现很多线程处于这个状态:    at jrockit/vm/Al ...

  3. python 异步发送邮件 aiosmtplib

    aiosmtplib is an asynchronous SMTP client for use with asyncio.文档地址 与 smtplib的用法大体相同 有几个地方需要注意下: 加密S ...

  4. tensorflow 学习纪录(持续更新)

    import tensorflow as tf import numpy as np #tensor = tf.constant([[1,2,3,4,5,6,7,8],[1,2,3,4,5,6,7,8 ...

  5. DW/BI领域的自动化调度方案

    1,利用专用自动化工具:如Tidal,Ctrl-M,Jenkins等.特点是功能强大,可以调用各种平台程序,脚本,可以设置依赖关系,调度时间,可以监控运行状态,发送邮件. 2,利用工具自身携带的调度功 ...

  6. width属性

    宽度属性 width:长度值|百分比|auto 最大宽度:max-width 最小宽度:min-width 说明:设置块级元素和替换元素的内容宽度.

  7. JAVA日常之三

    一.Main方法的args参数 args[] 是程序运行前可传入的参数,比如 java HelloWorld a,那么在HelloWorld的main方法里面 args就是{"a" ...

  8. 使用min-device-pixel-ratio媒体功能实现真正的1像素border

    关于设备像素比的知识,想必做过移动端开发的都有接触,这里就不介绍啦,万一有不懂的可以看张鑫旭大神的设备像素比devicePixelRatio简单介绍 由于设备像素比存在的原因,我们在处理设计图的一些边 ...

  9. ionic2+angularjs2

    感觉TypeScript真不错,强类型,有点类似c#的感觉,而且如果写错了编辑器都可以感知出来,于是就开始看ionic2.ionic2是基于angular2的,语法跟以前有了很大的变化.但自己写原生a ...

  10. 7th week blog

    DOM:Document Object Model(文档对象模型) Document Object Model的历史可以追溯至1990年代后期微软与Netscape的“浏览器大战”,双方为了在Java ...