case class 和class的区别以及构造器参数辨析

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

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

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

 1     class AA(name: String)
 2     class BB(val name: String)
 3     class CC(var name: String)
 4     class DD(private val name: String)
 5     class EE(private[this] val name: String)
 6     case class FF(name: String)
 7     case class GG(val name: String)
 8     case class HH(var name: String)
 9     case class II(private val name: String)
10     case class JJ(private[this] val name: String)

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

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

源码:

1     class AA(name: String)
2     class BB(val name: String)
3     class CC(var name: String)

反编译结果:

 1 Compiled from "Test.scala"
 2 public class AA {
 3   public AA(java.lang.String);
 4 }
 5
 6 Compiled from "Test.scala"
 7 public class BB {
 8   private final java.lang.String name;
 9   public java.lang.String name();
10   public BB(java.lang.String);
11 }
12
13 Compiled from "Test.scala"
14 public class CC {
15   private java.lang.String name;
16   public java.lang.String name();
17   public void name_$eq(java.lang.String);
18   public CC(java.lang.String);
19 }

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

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

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

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

源码:

1     class DD(private val name: String)
2     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的区别

源码:

1   case class FF(name: String)

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

反编译结果:

 1 Compiled from "Test.scala"
 2 public class FF implements scala.Product,scala.Serializable {
 3     //private final 修饰的name常量
 4     private final java.lang.String name;
 5     //public修饰获取name的方法,可用于外部访问
 6     public java.lang.String name();
 7     //public修饰的构造函数
 8     public FF(java.lang.String);
 9     public static scala.Option<java.lang.String> unapply(FF);
10     public static FF apply(java.lang.String);
11     public static <A> scala.Function1<java.lang.String, A> andThen(scala.Function1<FF, A>);
12     public static <A> scala.Function1<A, FF> compose(scala.Function1<A, java.lang.String>);
13     public FF copy(java.lang.String);
14     public java.lang.String copy$default$1();
15     public java.lang.String productPrefix();
16     public int productArity();
17     public java.lang.Object productElement(int);
18     public scala.collection.Iterator<java.lang.Object> productIterator();
19     public boolean canEqual(java.lang.Object);
20     public int hashCode();
21     public java.lang.String toString();
22     public boolean equals(java.lang.Object);
23 }
24
25 Compiled from "Test.scala"
26 public final class FF$ extends scala.runtime.AbstractFunction1<java.lang.String, FF> implements scala.Serializable {
27     //静态的FF$对象
28     public static FF$ MODULE$;
29     //构造函数为private
30     private FF$();
31
32     //返回FF对象的 apply方法
33     public FF apply(java.lang.String);
34
35     public static {};
36     public final java.lang.String toString();
37     public scala.Option<java.lang.String> unapply(FF);
38     private java.lang.Object readResolve();
39     public java.lang.Object apply(java.lang.Object);
40 }

分析:

  先看FF.class

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

1 public class AA
2 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修饰,可允许外部调用。

四、其他

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

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

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

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

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

-----end

原文地址:https://www.cnblogs.com/PerkinsZhu/p/9307460.html

时间: 2024-08-29 14:13:57

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

effective java读书札记值第2条:遇到多个构造器参数时要考虑用构造器

对于一个参数有多个的构造器的类,程序员一向习惯采用重叠的构造器模式. public class Person { private String Id; private String name; private String address; public Person(String id) { Id = id; } public Person(String name, String address) { super(); this.name = name; this.address = addr

【代码优化】构造器参数繁多时候,考虑使用builder模式

静态工厂和构造器有个共同的局限性:都不能很好的扩展到大量的可选参数. 1.对于多个可选参数的构造器,我们都习惯采用重叠构造器模式,比如一个参数的构造器调用2个参数的构造器,     2个参数的构造器调用3个参数的,以此类推. public class User{ private int id: private String name: private String age: private String sex: public User(int id){ this(id,null); } pub

第二条 遇到多个构造器参数时,要考虑用构建器

静态工厂和构造器都有个共同的局限性,就是它们都不能够很好的扩展到大量的可选参数. 如: public class NutritionFacts{ private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; public Nu

多个构造器参数使用构建器

标题一眼看过去可能不是很明白要讲什么,先来看看下面一段代码. 1 package example; 2 3 /** 4 * 重叠构造器 5 * @author yulinfeng 6 * Feb 13, 2017 7 */ 8 public class Student1 { 9 private String name; 10 private int age; 11 private String sex; 12 13 public Student1(String name) { 14 this(n

Effective Item 2 - 遇到多个构造器参数时考虑使用Builder

静态工厂和够构造器有一个共同的局限性:遇到大量的参数时无法很好的扩展. 先说说构造器. 其实field不多时重叠构造器(telescoping constructor)是个不错的方法,易于编写也易于调用,这种方式在参数数量较少时也很常见. 但问题是参数很多(可能越来越多)时,比如(现在已经很难找到对多个参数进行重叠构造的代码了,于是在这里直接引用一下书中的代码): public class NutritionFacts { private final int servingSize; // (m

第二条:遇到多个构造器参数时要考虑用构建器

静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数. 假如一份营养成分的标签,有两个域是必须的,四个域是可选的. 1.重叠构造器模式 提供一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个构造器有两个可选参数,依次类推,直到最后一个构造器包含所有的可选参数. 缺点:谁作为第一个可选参数是一个问题,因为后一个包含两个可选参数的构造器必须传递值给第一个可选参数,导致需要设置许多本不想设置的参数,像是一个极端的例子NutritionFacts n = new Nutrit

Scala 中 case class 与 class 的区别(代码示例)

class 类似Java中的class case class 被称为样例类,是一种也输的类,常被用于模式匹配 具体区别: 1,初始化的时候可以不用new ,也可以加上,但是普通类必须加上new 2,默认实现了equals.hashCode方法 3,默认是可以序列化的,实现了Serializable 4,case class 构造函数参数是public的,我们可以直接按访问 5,case class默认情况下不能修改属性值 7,case class 最重要的功能,支持模式匹配,这也是定义case

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

重载 发生在同一类,方法名必须相同,参数类型不同,顺序不同,类型不同,方法返回值和返回类型可以不同 重写 发生在子父类,方法名.参数名参数列表必须相同.返回值范围小于等于父类,抛出异常范围小于等于父类,访问修饰符大于等于父类.如果父类为private,则子类无法重写 构造器无法被重写,但是可以被重载,你可以看到一个类中有多个构造器的情况 原文地址:https://www.cnblogs.com/zzfpz/p/10990381.html

第二章:创建和销毁对象。ITEM2:遇到多个构造器参数时要考虑用构建器。

如果一个类中有大量的可选参数,有以下几种方式: 1.重叠构造器: package com.twoslow.cha2; /** * 重叠构造器可行,但是当由许多参数的时候,客户端代码很难编写. * @author sai * */ public class Item201 { private final int servingSize; private final int servings; private final int calories; private final int fat; pr