case class 和class的区别以及构造器参数辨析
工作中偶然发现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的区别以及构造器参数辨析的更多相关文章
- strong,weak, retain, assign的区别@property的参数
strong,weak, retain, assign的区别@property的参数 先说经验 使用场合 copy:NSString,block, weak:UI控件,代理 strong:一般对象.自 ...
- effective java 3th item2:考虑 builder 模式,当构造器参数过多的时候
yiaz 读书笔记,翻译于 effective java 3th 英文版,可能有些地方有错误.欢迎指正. 静态工厂方法和构造器都有一个限制:当有许多参数的时候,它们不能很好的扩展. 比如试想下如下场景 ...
- Spring Boot 构造器参数绑定,越来越强大了!
在之前的文章:Spring Boot读取配置的几种方式,我介绍到 Spring Boot 中基于 Java Bean 的参数绑定,在一个 Java Bean 类上用 @ConfigurationPro ...
- Java构建器(多个构造器参数)
今天看netty权威指南,第一次听说构建器,百度了几个博客,但是并没有通俗易懂一点儿的,综合别人的博客,总结如下: 1. 构建器是什么? 当创建对象需要传入多个参数的时候我们通常会根据参数的数量写不同 ...
- if else if,switch case二者的联系与区别
前段时间在学习中听到了一个关于条件判断语句的问题,分析if else if语句和switch case语句这两者之间的联系和区别,从而使用其中最有效率的一种方法. 一.if...else if if. ...
- 重载和重写的区别?构造器 Contructor 构造器是否可被 override?
重载 发生在同一类,方法名必须相同,参数类型不同,顺序不同,类型不同,方法返回值和返回类型可以不同 重写 发生在子父类,方法名.参数名参数列表必须相同.返回值范围小于等于父类,抛出异常范围小于等于父类 ...
- android 点滴记录 ICCID IMSI IMEI MEID 关系 和 区别,相关参数在什么情况下可以获取...
1:ICCID:Integrate circuit card identity 集成电路卡识别码(固化在手机SIM卡中) ICCID为IC卡的唯一识别号码,共有20位数字组成,其编码格式为:XXXXX ...
- Scala主构造器参数是否升级为成员与是否有get/set
1:主构造器前面添加val/var 关键字则升级为类成员,否则只是构造器中的一个参数而已. 2:private 修饰get/set方法权限,private var/val 成员变量,则有get/set ...
- 链式编程:遇到多个构造器参数(Constructor Parameters)时要考虑用构建器(Builder)
public class NutritionFacts { private final int servingSize; private final int servings; private fin ...
随机推荐
- 测试那些事儿-Jmeter介绍及使用
Jmeter与LR有啥区别? Jmeter工具组成部分: 1.资源生成器:用于生成测试过程中服务器,负载机的资源代码.(LR中的VuGen) 2.用户运行器:通常是一个脚本运行引擎,根据脚本要求模拟指 ...
- Qt 学习-----helloword
(参考:http://www.qter.org/portal.php?mod=view&aid=27&page=3) 1. 打开“文件→新建文件或项目”菜单项(也可以直接按下Ctrl+ ...
- new和malloc
1.申请的内存所在位置 自由存储区(free store)是C++基于new操作符的一个抽象概念,凡是new进行内存申请,该内存为自由存储区.堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于 ...
- memcached命令行、Memcached数据导出和导入、php连接memcache、php的session存储到memcached
1.memcached命令行 telnet 127.0.0.1 11211set key2 0 30 2abSTOREDget key2VALUE key2 0 2abEND 如: set key3 ...
- linux下tomcat的启动,停止,重启脚本
1.tomcat的启动脚本 #vi start_tomcat.sh pid=`ps -ef |grep tomcat |grep -v grep|awk '{print $2}'` ##定 ...
- 2018.5.4 Unix的五种IO模型
阻塞非阻塞和异步同步 同步和异步关注的是消息通信机制,关注两个对象之间的调用关系. 阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态,关注单一程序. Unix的五种IO模型 以下基于Li ...
- htop的安装和使用
1.Ubuntu16.04中htop安装很简单 sudo apt-get install htop 2.htop的使用,直接以普通用户输入htop命令即可执行 关于其基本介绍和使用方式在见下 http ...
- 2050 Programming Competition
http://2050.acmclub.cn/contests/contest_show.php?cid=3 开场白 Time Limit: 2000/1000 MS (Java/Others) ...
- Salesforce Lightning Builder Flows (Salesforce Lightning 构建Flows)
本文构建Salesforce Lightning Flows 只是一个简单的实现步骤,原文地址如下: https://trailhead.salesforce.com/en/content/learn ...
- python如何输出文件的年月日
import time print('{}BiasedMF312and4414_rt.txt'.format(time.strftime("%Y-%m-%d"))) 输出: 201 ...