kcheng.mvp
kcheng.mvp

Reputation: 1

scala primary constructor does not generate field

Here is the scala code

class Dog(name:String, age:Int) {
   println("Dog is created!")
   def sayHello() = println(s"My name is $name, I am $age years old")
}

decompile the generated class file as below.

import scala.Predef.;
import scala.StringContext;
import scala.reflect.ScalaSignature;
import scala.runtime.BoxesRunTime;
@ScalaSignature(bytes="\006\001=2A!\001\002\001\023\t\031Ai\\4\013\005\r!\021\001C2iCB$XM\035\034\013\005\0251\021!B6d[2\004(\"A\004\002\007\r|Wn\001\001\024\005\001Q\001CA\006\017\033\005a!\"A\007\002\013M\034\027\r\\1\n\005=a!AB!osJ+g\r\003\005\022\001\t\005\t\025!\003\023\003\021q\027-\\3\021\005MQbB\001\013\031!\t)B\"D\001\027\025\t9\002\"\001\004=e>|GOP\005\00331\ta\001\025:fI\0264\027BA\016\035\005\031\031FO]5oO*\021\021\004\004\005\t=\001\021\t\021)A\005?\005\031\021mZ3\021\005-\001\023BA\021\r\005\rIe\016\036\005\006G\001!\t\001J\001\007y%t\027\016\036 \025\007\025:\003\006\005\002'\0015\t!\001C\003\022E\001\007!\003C\003\037E\001\007q\004C\003+\001\021\0051&\001\005tCfDU\r\0347p)\005a\003CA\006.\023\tqCB\001\003V]&$\b")
public class Dog
{
  private final String name;

  public Dog(String name, int age)
 {
    Predef..MODULE$.println("Dog is created!");
  }

 public void sayHello()
{
  Predef..MODULE$.println(new StringContext(Predef..MODULE$.wrapRefArray((Object[])new String[] { "My name is ", ", I am ", " years old" })).s(Predef..MODULE$.genericWrapArray(new Object[] { this.name, BoxesRunTime.boxToInteger(this.age) })));
 }
}

There is only a field "name", but there is no "age" field. Why?

Upvotes: 0

Views: 125

Answers (2)

Henrik Sachse
Henrik Sachse

Reputation: 54322

I compiled/disassembled your code snippet the following way (on a Mac with Scala 2.12.1 and Java 1.8.0_112):

$ echo > Dog.scala <<EOF
class Dog(name:String, age:Int) {
  println("Dog is created!")
  def sayHello() = println(s"My name is $name, I am $age years old")
}
EOF
$ scalac Dog.scala && javap -private Dog.class

That results into the following output which contains both private final class members for name and age:

Compiled from "Dog.scala"
public class Dog {
  private final java.lang.String name;
  private final int age;
  public void sayHello();
  public Dog(java.lang.String, int);
}

So how did you compile/decompile your code?

In your question you wrote about fields. Actually in your case those are not fields, they are simple class parameters. They are only used by the constructor. It would be a field if you use the val keyword explicitly.

In case you want those parameters to be real fields accessible via dog.age and dog.name, write the following:

class Dog(val name:String, val age:Int) {
  println("Dog is created!")
  def sayHello() = println(s"My name is $name, I am $age years old")
}

As an alternative you could use a case class like:

case class Dog(name:String, age:Int) {
  println("Dog is created!")
  def sayHello() = println(s"My name is $name, I am $age years old")
}

For more details please have a look at the Functional Objects chapter in the Programming in Scala book.

EDIT: I just tried to decompile the same class file with the JD-GUI. That lead into the missing private age class member:

import scala.Predef.;
import scala.StringContext;
import scala.reflect.ScalaSignature;
import scala.runtime.BoxesRunTime;

@ScalaSignature(bytes="\006\001-2A!\001\002\001\013\t\031Ai\\4\013\003\r\tq\001P3naRLhh\001\001\024\005\0011\001CA\004\013\033\005A!\"A\005\002\013M\034\027\r\\1\n\005-A!AB!osJ+g\r\003\005\016\001\t\005\t\025!\003\017\003\021q\027-\\3\021\005=1bB\001\t\025!\t\t\002\"D\001\023\025\t\031B!\001\004=e>|GOP\005\003+!\ta\001\025:fI\0264\027BA\f\031\005\031\031FO]5oO*\021Q\003\003\005\t5\001\021\t\021)A\0057\005\031\021mZ3\021\005\035a\022BA\017\t\005\rIe\016\036\005\006?\001!\t\001I\001\007y%t\027\016\036 \025\007\005\032C\005\005\002#\0015\t!\001C\003\016=\001\007a\002C\003\033=\001\0071\004C\003'\001\021\005q%\001\005tCfDU\r\0347p)\005A\003CA\004*\023\tQ\003B\001\003V]&$\b")
public class Dog
{
    private final String name;

    public Dog(String name, int age)
    {
        Predef..MODULE$.println("Dog is created!");
    }

    public void sayHello()
    {
        Predef..MODULE$.println(new StringContext(Predef..MODULE$.wrapRefArray((Object[])new String[] { "My name is ", ", I am ", " years old" })).s(Predef..MODULE$.genericWrapArray(new Object[] { this.name, BoxesRunTime.boxToInteger(this.age) })));
    }
}

So probably this is a bug in JD since javap worked. I created a ticket on github. But I doubt that they will fix it. The last commit on the JD-GUI project was more than a year ago...

Upvotes: 0

Akash Sethi
Akash Sethi

Reputation: 2294

Thats Strange because when I decompiled the above code I got this.

import scala.Predef;
import scala.StringContext;
import scala.collection.Seq;
import scala.collection.mutable.WrappedArray;
import scala.reflect.ScalaSignature;
import scala.runtime.BoxesRunTime;

@ScalaSignature(bytes="\u0006\u0001\u001d2A!\u0001\u0002\u0001\u000b\t\u0019Ai\\4\u000b\u0003\r\tq\u0001P3naRLhh\u0001\u0001\u0014\u0005\u00011\u0001CA\u0004\u000b\u001b\u0005A!\"A\u0005\u0002\u000bM\u001c\u0017\r\\1\n\u0005-A!AB!osJ+g\r\u0003\u0005\u000e\u0001\t\u0005\t\u0015!\u0003\u000f\u0003\u0011q\u0017-\\3\u0011\u0005=\u0011bBA\u0004\u0011\u0013\t\t\u0002\"\u0001\u0004Qe\u0016$WMZ\u0005\u0003'Q\u0011aa\u0015;sS:<'BA\t\t\u0011!1\u0002A!A!\u0002\u00139\u0012aA1hKB\u0011q\u0001G\u0005\u00033!\u00111!\u00138u\u0011\u0015Y\u0002\u0001\"\u0001\u001d\u0003\u0019a\u0014N\\5u}Q\u0019Qd\b\u0011\u0011\u0005y\u0001Q\"\u0001\u0002\t\u000b5Q\u0002\u0019\u0001\b\t\u000bYQ\u0002\u0019A\f\t\u000b\t\u0002A\u0011A\u0012\u0002\u0011M\f\u0017\u0010S3mY>$\u0012\u0001\n\t\u0003\u000f\u0015J!A\n\u0005\u0003\tUs\u0017\u000e\u001e")
public class Dog {
    private final String name;
    private final int age;

    public void sayHello() {
        Predef..MODULE$.println((Object)new StringContext((Seq)Predef..MODULE$.wrapRefArray((Object[])new String[]{"My name is ", ", I am ", " years old"})).s((Seq)Predef..MODULE$.genericWrapArray((Object)new Object[]{this.name, BoxesRunTime.boxToInteger((int)this.age)})));
    }

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
        Predef..MODULE$.println((Object)"Dog is created!");
    }
}

By looking at this you can see that both name and age class fields are available with private and final

In the constructor you can see that name, age are assigned there values and then the print statement is working.

I decompiled the above code online from http://www.javadecompilers.com/

Hope this clears you doubt.

Upvotes: 1

Related Questions