工作中偶然发现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. 网赚app

    网赚app有很多目前来说做的比较好的赚的比较多的有四款推荐 宝石星球下载地址:http://www.baoshixingqiu.com/redPacket?key=548341 雪梨网APP下载地址 ...

  2. pytorch怎么抽取中间的特征或者梯度

    for i, (input, target) in enumerate(trainloader): # measure data loading time data_time.update(time. ...

  3. ROS使用小知识点

    输入 rosrun rqt_graph rqt_graph 可以打开一个界面观察节点与话题的关系 绿色和蓝色的是节点 红色的是话题 查看ros中额的tf转换信息 rosrun rqt_tf_tree ...

  4. linux为什么要使用CentOS开发?

    CentOS(Community Enterprise Operating System,社区企业操作系统)是Linux发行版之一,它是来自于Red Hat Enterprise Linux依照开放源 ...

  5. SpringBoot框架中,使用过滤器进行加密解密操作(一)

    一.基本说明 1.请求方式:POST请求.注解@PostMapping 2.入参格式:json串 3.出参格式:json串(整体加密) 4.使用Base64进行加密解密.具体的加密方式,可以根据需求自 ...

  6. Hibernate5.3 + mysql8.0遇到的问题

    今天学习Hibernate看的是旧版本的视频Hibernate4.0版本 遇到几个新旧版本的区别. 1.方言,这个是因为SQL不是因为Hibernate 新版方言 2.将编译的类配置进congifur ...

  7. 运行Tomcat报错 解决方法

    The APR based Apache Tomcat Native library which allows optimal performance in production environmen ...

  8. jquery的相关用法

    选择器基本选择器1.id选择器$('#id1')找到id为id1 的标签2.class选择器$('.class1')找到class中有class1这个类的标签3.标签选择器$('tag') 找到tag ...

  9. Codeforces Round #350 (Div. 2) C. Cinema

    Moscow is hosting a major international conference, which is attended by n scientists from different ...

  10. Python线程的用法 函数式线程_thread和threading 样例

    函数式线程写起来比较简单,但是功能没有threading那么高级,先来个函数式编程样例: #!/usr/bin/python #coding: utf-8 #————————————————————— ...