如何在Scala中构造对象


Scala的面向对象编程风格带有一些语法糖和一些技巧。

为了更好地了解Scala的工作原理,我们将研究一些Scala示例和相应的字节代码(实际上是反编译类文件所产生的Java代码)。

Examples Empty Class 最简单的例子,一个空的类。这里只有一个带有无参数构造函数的类,这不足为奇。顺便说一句,如本例所示,Scala中的默认可见性修饰符是public

//Scala
class Empty
//Java
public class Empty {}

有参数但没有字段的类 带有参数的构造函数的类:它导致带有all-args构造函数的类。 任何方法之外的类的主体都被视为构造函数定义的一部分。

//Scala
class Fraction(n: Int, d: Int) {
  System.out.println("numerator:" + n + ", denominator:" + d)
}
//Java
public Fraction(final int n, final int d) {
  System.out.println("numerator:" + n + ", denominator:" + d);
}

具有参数提升为字段的类 当构造函数的参数由构造函数以外的类的成员使用时,这些参数将提升为字段,以便可以对其进行访问。

//Scala
class FractionString(n: Int, d: Int){
  override def toString: String = n + "/" + d
}
//Java
public class FractionString {
   private final int n;
   private final int d;
   public String toString() {
      return this.n + "/" + this.d;
   }
   public FractionString(final int n, final int d) {
      this.n = n;
      this.d = d;
   }
}

值得注意的是,生成的字段的可见性是对象专用的(private [this]),这意味着它们不能被同一类的其他对象访问。例如,此示例将无法编译:

//Scala
class FractionEqual(n: Int, d: Int){
  override def equals(other: Any): Boolean = other match {
    case that: FractionEqual => n == that.n && d == that.d
    case _ => false
  }
}
Error:(32, 43) value n is not a member of FractionEqual
    case that: FractionEqual => n == that.n && d == that.d

有趣的是,修饰符private [this]在JVM中没有意义,因此被编译为private。

带参数字段的类 关键字val可以用作同时定义参数和名称相同的字段的简写形式。该字段伴随有一个名为字段的吸气剂。

//Scala
class FractionVal(val n: Int, val d: Int)
//Java
public class FractionVal {
   private final int n;
   private final int d;
   public int n() {
      return this.n;
   }
   public int d() {
      return this.d;
   }
   public FractionVal(final int n, final int d) {
      this.n = n;
      this.d = d;
   }
}

但是,所有设计都是为了制造一种幻想,即没有方法。在这种情况下,将getter定义为无参数方法,因此客户端将忽略他们正在调用val还是def。 这是Scala支持统一访问原则的方式:

客户端代码不应受到将属性实现为字段或方法的决定的影响。

不幸的是,这在Java版本中无法理解,因为它显示了用括号定义的方法,一个空括号的方法,因为在JVM领域中不存在无参数方法。

但是,以下示例说明了它在Scala中的工作方式:

scala> class FractionVal(val n: Int, val d: Int)
defined class FractionVal
scala> val f = new FractionVal(1,2
f: FractionVal = FractionVal@2102eb7a
scala> f.n
res17: Int = 1
scala> f.d
res18: Int = 2
scala> f.n()
<console>:13: error: Int does not take parameters
       f.n()

现在,将有可能实现的方法等于S作为字段都可以通过相应的公共访问干将。

带有var的参数字段 也可以使用关键字var定义参数字段。在这种情况下,还将生成一个名为field_ =的设置器。

//Scala
class FractionVar(var n: Int, var d: Int)
//Java
public class FractionVar {
   private int n;
   private int d;
   public int n() {
      return this.n;
   }
   public void n_$eq(final int x$1) {
      this.n = x$1;
   }
   public int d() {
      return this.d;
   }
   public void d_$eq(final int x$1) {
      this.d = x$1;
   }
   public FractionVar(final int n, final int d) {
      this.n = n;
      this.d = d;
      super();
   }
}

与前面的示例类似,将setter field_ =解释为赋值运算符,以产生没有方法的错觉。尽管在这种情况下,也可以直接调用设置器:

scala> class FractionVar(var n: Int, var d: Int)
defined class FractionVar
scala> val f = new FractionVar(1,2)
f: FractionVar = FractionVar@1ecdfe1e
scala> f.n
res20: Int = 1
scala> f.n = 9
f.n: Int = 9
scala> f.n
res21: Int = 9
scala> f.n_=(10)
scala> f.n
res24: Int = 10

生成的getter / setter 的可见性与相应的val / var的可见性相同

带有自定义Getter / Setter的类 按照Java的传统,我们还可以创建自己的getter / setter方法。但是,对变量的访问仍然是通过合成的getter / setter完成的。

//Scala
class FractionVar(var n: Int, var d: Int){
  def setN(n: Int) = this.n = n
  def getN() = n
}
//Java
public class FractionVar {
   private int n;
   private int d;
   public int n() {
      return this.n;
   }
   public void n_$eq(final int x$1) {
      this.n = x$1;
   }

   public int d() {
      return this.d;
   }
   public void d_$eq(final int x$1) {
      this.d = x$1;
   }
   public void setN(final int n) {
      this.n_$eq(n);
   }
   public int getN() {
      return this.n();
   }
   public FractionVar(final int n, final int d) {
      this.n = n;
      this.d = d;
      super();
   }
}

这是有关如何使用新方法的示例。注意如何在带或不带括号的情况下调用getN

scala> :paste
// Entering paste mode (ctrl-D to finish)
class FractionVar(var n: Int, var d: Int){
  def setN(n: Int) = this.n = n
  def getN() = n
}
// Exiting paste mode, now interpreting.
defined class FractionVar
scala> val f = new FractionVar(1,2)
f: FractionVar = FractionVar@125490ea
scala> f.getN()
res26: Int = 1
scala> f.getN
res27: Int = 1
scala> f.setN(9)
scala> f.getN
res29: Int = 9

Object 对象被编译为两个类:

名称后缀为$并包含实际功能的单例 具有静态转发器的类,以访问单例功能。

//Scala
object FractionObject {
  def fraction(n: Int, d: Int) = n + "/" + d
}
//Java
public final class FractionObject$ {
   public static FractionObject$ MODULE$;

   static {
      new FractionObject$();
   }

   public String fraction(final int n, final int d) {
      return n + "/" + d;
   }

   private FractionObject$() {
      MODULE$ = this;
   }
}

public final class FractionObject {
   public static String fraction(int var0, int var1) {
      return FractionObject$.MODULE$.fraction(var0, var1);
   }
}

Companion Object Scala中的伴随对象是与类在同一文件中声明的对象,并且与该类具有相同的名称。与前面的情况类似,创建了一个单例和一个转发器。 唯一的区别是,现在单例实现了AbstractFunction(具有相应的-arity)和Serializable。

//Scala
class FractionVal(val n: Int, val d: Int)
object FractionVal
//Java
public final class FractionVal$ extends AbstractFunction2 implements Serializable {
   public static FractionVal$ MODULE$;
   static {
     new FractionVal$();
   }
   public final String toString() {
      return "FractionVal";
   }
   public FractionVal apply(final int n, final int d) {
      return new FractionVal(n, d);
   }

   public Option unapply(final FractionVal x$0) {
      return (Option)(x$0 == null ? .MODULE$ : new Some(new sp(x$0.n(), x$0.d())));
   }

   private Object readResolve() {
      return MODULE$;
   }
   // $FF: synthetic method
   // $FF: bridge method
   public Object apply(final Object v1, final Object v2) {
      return this.apply(BoxesRunTime.unboxToInt(v1), BoxesRunTime.unboxToInt(v2));
   }

   private FractionVal$() {
      MODULE$ = this;
   }
}

public class FractionVal {
   private final int n;
   private final int d;

   public int n() {
      return this.n;
   }

   public int d() {
      return this.d;
   }

   public FractionVal(final int n, final int d) {
      this.n = n;
      this.d = d;
   }
}

有趣的是,由于FractionVal没有任何对FractionVal $的引用,因此在Scala中无法使用单例实现的功能。 例如,方法apply不可用,而toString使用Object.toString给定的实现,而不是FractionVal $ .toString

scala> :paste
// Entering paste mode (ctrl-D to finish)
class FractionVal(val n: Int, val d: Int)
object FractionVal
// Exiting paste mode, now interpreting.
defined class FractionVal
defined object FractionVal

scala> FractionVal(1,2)
<console>:13: error: FractionVal.type does not take parameters
       FractionVal(1,2)
                  ^

scala> new FractionVal(1,2)
res5: FractionVal = FractionVal@7f2c995b

Case Class 案例类是具有大量功能的常规类。

  • 默认情况下,构造函数参数为val
  • 对于每个案例类,编译器都会创建一个伴随对象
  • 结果类实现Serializable
  • 基于类字段的属性覆盖equal和hashCode方法

这是结果类的结构:

1604600834308.png

//Scala
case class FractionVal(n: Int, d: Int)
//Java
import scala.Option;
import scala.Serializable;
import scala.Some;
import scala.None.;
import scala.Tuple2.mcII.sp;
import scala.runtime.AbstractFunction2;
import scala.runtime.BoxesRunTime;
public final class FractionVal$ extends AbstractFunction2 implements Serializable {
   public static FractionVal$ MODULE$;
   static {
      new FractionVal$();
   }

   public final String toString() {
      return "FractionVal";
   }

   public FractionVal apply(final int n, final int d) {
      return new FractionVal(n, d);
   }

   public Option unapply(final FractionVal x$0) {
      return (Option)(x$0 == null ? .MODULE$ : new Some(new sp(x$0.n(), x$0.d())));
   }
   private Object readResolve() {
      return MODULE$;
   }
   // $FF: synthetic method
   // $FF: bridge method
   public Object apply(final Object v1, final Object v2) {
      return this.apply(BoxesRunTime.unboxToInt(v1), BoxesRunTime.unboxToInt(v2));
   }
   private FractionVal$() {
      MODULE$ = this;
   }
}
import scala.Function1;
import scala.Option;
import scala.Product;
import scala.Serializable;
import scala.collection.Iterator;
import scala.reflect.ScalaSignature;
import scala.runtime.BoxesRunTime;
import scala.runtime.Statics;
import scala.runtime.ScalaRunTime.;
public class FractionVal implements Product, Serializable {

   private final int n;
   private final int d;
   public static Option unapply(final FractionVal x$0) {
      return FractionVal$.MODULE$.unapply(var0);
   }

   public static FractionVal apply(final int n, final int d) {
      return FractionVal$.MODULE$.apply(var0, var1);
   }

   public static Function1 tupled() {

      return FractionVal$.MODULE$.tupled();
   }

   public static Function1 curried() {
      return FractionVal$.MODULE$.curried();
   }

   public int n() {
      return this.n;
   }

   public int d() {
      return this.d;
   }

   public FractionVal copy(final int n, final int d) {
      return new FractionVal(n, d);
   }

   public int copy$default$1() {
      return this.n();
   }

   public int copy$default$2() {
      return this.d();
   }

   public String productPrefix() {
      return "FractionVal";
   }

   public int productArity() {
      return 2;
   }

   public Object productElement(final int x$1) {
      Integer var10000;
      switch(x$1) {
      case 0:
         var10000 = BoxesRunTime.boxToInteger(this.n());
     break;
      case 1:

         var10000 = BoxesRunTime.boxToInteger(this.d());
         break;
      default:
         throw new IndexOutOfBoundsException(BoxesRunTime.boxToInteger(x$1).toString());
      }

      return var10000;
   }

   public Iterator productIterator() {
      return .MODULE$.typedProductIterator(this);

   }

   public boolean canEqual(final Object x$1) {
      return x$1 instanceof FractionVal;
   }

   public int hashCode() {
      int var1 = -889275714;
      var1 = Statics.mix(var1, this.n());
      var1 = Statics.mix(var1, this.d());
      return Statics.finalizeHash(var1, 2);
   }

   public String toString() {

      return .MODULE$._toString(this);
   }

   public boolean equals(final Object x$1) {
      boolean var10000;
      if (this != x$1) {
         label51: {
            boolean var2;
            if (x$1 instanceof FractionVal) {
               var2 = true;
            } else {
               var2 = false;
            }

            if (var2) {
               FractionVal var4 = (FractionVal)x$1;
               if (this.n() == var4.n() && this.d() == var4.d() && var4.canEqual(this)) {
                  break label51;
               }
            }
            var10000 = false;
            return var10000;
         }
      }

      var10000 = true;
      return var10000;
   }

   public FractionVal(final int n, final int d) {
      this.n = n;
      this.d = d;
      Product.$init$(this);
   }
}

Case Object 也可以定义案例对象。再次,创建一个单例和一个转发器。最值得注意的是,单例实现Serializable和Product。

//Scala
`
case object FractionObject
//Java
import scala.collection.Iterator;
public final class FractionObject {
   public static String toString() {
      return FractionObject$.MODULE$.toString();
   }

   public static int hashCode() {
      return FractionObject$.MODULE$.hashCode();
   }

   public static boolean canEqual(final Object x$1) {
      return FractionObject$.MODULE$.canEqual(var0);
   }

   public static Iterator productIterator() {
      return FractionObject$.MODULE$.productIterator();
   }

   public static Object productElement(final int x$1) {
      return FractionObject$.MODULE$.productElement(var0);
   }

   public static int productArity() {
      return FractionObject$.MODULE$.productArity();
   }

   public static String productPrefix() {
      return FractionObject$.MODULE$.productPrefix();
   }

}
import scala.Product;
import scala.Serializable;
import scala.collection.Iterator;
import scala.runtime.BoxesRunTime;
import scala.runtime.ScalaRunTime.;

public final class FractionObject$ implements Product, Serializable {

   public static FractionObject$ MODULE$;

   static {
      new FractionObject$();
   }

   public String productPrefix() {

      return "FractionObject";
   }


   public int productArity() {
      return 0;

   }

   public Object productElement(final int x$1) {
      throw new IndexOutOfBoundsException(BoxesRunTime.boxToInteger(x$1).toString());
   }

   public Iterator productIterator() {
      return .MODULE$.typedProductIterator(this);
   }

   public boolean canEqual(final Object x$1) {
      return x$1 instanceof FractionObject$;
   }
   public int hashCode() {
      return 1259148289;
   }

   public String toString() {
      return "FractionObject";
   }

   private Object readResolve() {
      return MODULE$;
   }
   private FractionObject$() {
      MODULE$ = this;
      Product.$init$(this);
   }
}

Trait Trait被编译为接口

//Scala
trait FractionTrait{
  val n: Int
  val d: Int
}
//Java
public interface FractionTrait {
   int n();
   int d();
}

Abstract Class 具体类的规则也适用于抽象类

//Scala
abstract class AbstractFraction(val n: Int, val d: Int)
//Java
public abstract class AbstractFraction {
   private final int n;
   private final int d;
   public int n() {
      return this.n;
   }

   public int d() {
      return this.d;
   }

   public AbstractFraction(final int n, final int d) {
      this.n = n;
      this.d = d;
   }
}

Package Object 包对象被视为对象。创建两类:一单叫包$和代理类称为包

//Scala
package object mypackage {
  def greet() = "hello"
}
//Java
package mypackage;
public final class package {
   public static String greet() {
      return package$.MODULE$.greet();
   }
}
public final class package$ {
   public static package$ MODULE$;

   static {
      new package$();
   }

   public String greet() {
      return "hello";
   }

   private package$() {
      MODULE$ = this;
   }
}


原文链接:http://codingdict.com