Consider the following class written in Java:
class NonNegativeDouble {
private final double value;
public NonNegativeDouble(double value) {
this.value = Math.abs(value);
}
public double getValue() { return value; }
}
It defines a final field called value that is initialized in the constructor, by taking its parameter called alike and applying a function to it.
I want to write something similar to it in Scala. At first, I tried:
class NonNegativeDouble(value: Double) {
def value = Math.abs(value)
}
But the compiler complains: error: overloaded method value needs result type
Obviously the compiler thinks that the expression value inside the expression Math.abs(value) refers to the method being defined. Therefore, the method being defined is recursive, so I need to state its return type. So, the code I wrote does not do what I expected it to do: I wanted value inside Math.abs(value) to refer to the constructor parameter value, and not to the method being defined. It is as if the compiler implicitly added a this. to Math.abs(this.value).
Adding val or var (or private ... variants) to the constructor parameter doesn't seem to help.
So, my question is: can I define a property with the same name as a constructor parameter, but maybe a different value? If so, how? If not, why?
Thanks!
No, you can't. In Scala, constructor parameters are properties, so it makes no sense to redefine them.
The solution, naturally, is to use another name:
class NonNegativeDouble(initValue: Double) {
val value = Math.abs(initValue)
}
Used like this, initValue won't be part of the instances created. However, if you use it in a def or a pattern matching declaration, then it becomes a part of every instance of the class.
#Daniel C. Sobral
class NonNegativeDouble(initValue: Double) {
val value = Math.abs(initValue)
}
your code is right, but "constructor parameters are properties",this is not true.
A post from the official site said,
A parameter such as class Foo(x : Int) is turned into a field if it is
referenced in one or more methods
And Martin's reply confirms its truth:
That's all true, but it should be treated as an implementation
technique. That's why the spec is silent about it.
So normally, we can still treat primary constructor parameters as normal method parameter, but when the parameters is referenced by any of the methods, the compiler will cleverly turn it into a private field.
If any formal parameter preceded by the val, the compiler generates an getter definition automatically.if var, generates a setter additionally. see the language speification section 5.3.
That's all about primary constructor parameters.
You can consider parametric field
class NonNegativeDouble(val value: Double, private val name: String ){
if (value < 0) throw new IllegalArgumentException("value cannot be negative")
override def toString =
"NonNegativeDouble(value = %s, name = %s)" format (value, name)
}
val tom = "Tom"
val k = -2.3
val a = new NonNegativeDouble(k.abs, tom)
a: NonNegativeDouble = NonNegativeDouble(value = 2.3, name = Tom)
a.value
res13: Double = 2.3
a.name
<console>:12: error: value name in class NonNegativeDouble cannot be accessed in NonNegativeDouble
a.name
val b = new NonNegativeDouble(k, tom)
java.lang.IllegalArgumentException: value cannot be negative
...
It's defines fields and parameters with the same names "value", "name".
You can add modifiers such as private ...
In the case of case classes it should be:
case class NonNegativeDouble(private val initValue: Double) {
val value = Math.abs(initValue)
def copy(value: Double = this.value) = NonNegativeDouble(value)
}
The implementation of copy is required to prevent the sintesized version of the compiler that will bind the initValue argument.
I expect that the compiler is smart enough to not retain the «extra space» for the initValue. I haven't verified this behaviour.
Related
I'm trying to use discriminators in existing project and something is wrong with my classes I guess.
Consider this scodec example. If I change TurnLeft and its codec to
sealed class TurnLeft(degrees: Int) extends Command {
def getDegrees: Int = degrees
}
implicit val leftCodec: Codec[TurnLeft] = uint8or16.xmap[TurnLeft](v => new TurnLeft(v), _.getDegrees)
I get
Error:(x, x) could not find Lazy implicit value of type scodec.Codec[Command]
val codec: Codec[Either[UnrecognizedCommand, Command]] = discriminatorFallback(unrecognizedCodec, Codec[Command])
It all works if I make degrees field value field. I suspect it's something tricky with shapeless. What should I do to make it work ?
Sample project that demonstrates the issue is here.
shapeless's Generic is defined for "case-class-like" types. To a first approximation, a case-class-like type is one whose values can be deconstructed to it's constructor parameters which can then be used to reconstruct an equal value, ie.
case class Foo ...
val foo = Foo(...)
val fooGen = Generic[Foo]
assert(fooGen.from(fooGen.to(foo)) == foo)
Case classes with a single constructor parameter list meet this criterion, whereas classes which don't have public (lazy) vals for their constructor parameters, or a companion with a matching apply/unapply, do not.
The implementation of Generic is fairly permissive, and will treat (lazy) val members which correspond to constructor parameters (by type and order) as being equivalent to accessible constructor arguments, so the closest to your example that we can get would be something like this,
sealed class TurnLeft(degrees: Int) extends Command {
val getDegrees: Int = degrees
}
scala> Generic[TurnLeft]
res0: shapeless.Generic[TurnLeft]{type Repr = Int :: HNil } = ...
In this case getDegrees is treated as the accessor for the single Int constructor parameter.
What is the different between Option[String] and Option[String] = None? I think Option type is default to None if not set, but not 100% sure. Wondering if there will be any potential impact.
For example:
case class User(name:Option[String])
Will it be different from
case class User(name:Option[String]=None)
I think you confuse two different concepts: Default arguments and Default initial values.
Default arguments
In Scala you can provide default arguments for parameters of methods and constructors. And it allows you to skip those arguments:
case class User(name:Option[String]=Some("Sam")) {
def say(greeting: String = "Hello"): String = greeting + " " + name
}
val u = new User
println(u.say())
As you can see, it has nothing to do with Option and None. It works for any type (see the method) and yes, you have to specify that default yourself, otherwise it will not compile:
case class User(name:Option[String])
val u = new User
// Doesn't compile.
And no, None is not the default value for Option[A] when we talk about default arguments. You can provide None as well as any other value of type Option[A] as a parameter of type Option[A], but you have to do it explicitly:
case class User(name:Option[String]=Some("Sam"))
case class User(name:Option[String]=None)
So, talking about parameters to methods and constructors, there's no default value for values of type Option[A]. You must specify that default yourself, otherwise it will not compile.
So, these two lines from your question are indeed different:
case class User(name:Option[String])
case class User(name:Option[String]=None)
Default initial values
Consider this code:
var name: Option[String] = _
println(name)
null is printed. Because the default value is null, not None. And this is the case not only for Option[A], but for all AnyRef types in Scala. The following prints null either:
var name: String = _
println(name)
Please, refer also to Scala Language Specification. E.g. https://www.scala-lang.org/files/archive/spec/2.11/04-basic-declarations-and-definitions.html#variable-declarations-and-definitions
Your assumption is incorrect. There is no standard default for parameters; usually, parameters that are declared must also be provided explicitly when the method or constructor is called.
But a parameter can be declared as optional, and supplied with a default value at its declaration point: When you declare a parameter with its name and type, and then you add the equals sign followed by a static value, then this parameter becomes optional. If left out, the given static value will be inserted automatically.
This does not only work with case classes, but also with other kinds of constructors and methods.
In the first example that you have given, a statement like val user = User() would not compile, because the name parameter must be given.
In the second example, val user = User() would very well compile. At runtime, user.name would have the value of None.
It´s most probably a stupid question, I saw an answer already here but seems does not work in my particular case. Here my code
object ProductAdded{
val PRODUCT_NAME:String="productName"
val PRODUCT_PRICE:String="productPrice"
}
class ProductAdded #JsonCreator()(#JsonProperty(PRODUCT_NAME) var productName: String,
#JsonProperty(PRODUCT_PRICE) var productPrice: String) extends Event {
#JsonProperty(PRODUCT_NAME) def getProductName: String = {
productName
}
#JsonProperty(PRODUCT_PRICE) def getProductPrice: String = {
productPrice
}
}
I´m trying to use those constants in the constructor, but when I compile show this error
Error:(12, 49) annotation argument needs to be a constant; found: event.ProductAdded.PRODUCT_NAME
class ProductAdded #JsonCreator()(#JsonProperty(PRODUCT_NAME) var productName: String,
This is a pretty unfortunate error message, I must say, since Scala doesn't actually have a concept named "constant", so how are you supposed to make something a "constant" if you don't even know what it is?
Scala does, however, have a concept of "constant expression" (bold emphasis mine):
6.24 Constant Expressions
Constant expressions are expressions that the Scala compiler can evaluate to a constant. The definition of "constant expression" depends on the platform, but they include at least the expressions of the following forms:
A literal of a value class, such as an integer
A string literal
A class constructed with Predef.classOf
An element of an enumeration from the underlying platform
A literal array, of the form Array(c1, …, cn), where all of the ci's are themselves constant expressions
An identifier defined by a constant value definition.
and "constant value definition" (bold emphasis mine):
A constant value definition is of the form
final val x = e
where e is a constant expression. The final modifier must be present and no type annotation may be given.
So, as you can see, your expression is not a constant value expression, because the definition is not a constant value definition.
In order to turn it into a constant value definition, you need to
remove the type annotation : String
add the final modifier explicitly: even though members of a singleton object are implicitly final, constant value definitions require an explicit annotation
The result would look something like this:
object ProductAdded {
final val PRODUCT_NAME = "productName"
final val PRODUCT_PRICE = "productPrice"
}
class ProductAdded #JsonCreator()(
#JsonProperty(PRODUCT_NAME) var productName: String,
#JsonProperty(PRODUCT_PRICE) var productPrice: String
) extends Event {
#JsonProperty(PRODUCT_NAME) def getProductName = productName
#JsonProperty(PRODUCT_PRICE) def getProductPrice = productPrice
}
I found an example about abstract type member in Odersky's paper (Chapter 2.1): http://lampwww.epfl.ch/~odersky/papers/ScalableComponent.pdf
I paste it below
abstract class AbsCell {
type T
val init: T
private var value: T = init
def get: T = value
def set(x:T):Unit = {value = x}
}
val cell = new AbsCell{ type T=Int; val init=1}
cell.set(cell.get + 1)
cell.get
The codes doesn't work as expected in the latest Scala (Scala Version: 2.11).
I found the value of the last expression cell.get is 1, while what I expected is 2. The reason is that the private var value: T = init doesn't work well for the mixin anonymous class { type T=Int; val init=1}.
Does anyone have any ideas about this?
Your code doesn't work because value is initialized before the val init is. So at the time value is initialized, init is still equal to the default value of type T, which is 0 for T = Int.
You can fix this by making init a def or a lazy val.
Note that the paper you reference does not have the code you mention. In the paper, init is a parameter to the constructor of GenCell/AbsCell, and is therefore initialized properly before value is.
I have read that null should not be used in scala.
How can I leave myVar uninitialized without the use of null?
class TestClass {
private var myVar: MyClass = null
}
I understand I can just make a dummy MyClass, that is never used in place of the null. But this can and does reduce the code's understandability.
As Rado has explained I can change null as shown below. I understand that I can now check to see if the variable is set during run-time, however, if I don't program that check then there is no benefit of using Option in this case.
Coming from Java, I feel there should be a way to simply leave the var uninitialized at compile-time and let it set during run-time without using the Option class, because as I mentioned above, if I don't code for the unset case then why use Option?
class TestClass {
private var myVar: Option[MyClass] = None
private def createVar() {
myVar = Some(new MyClass)
x: MyClass = myVar.get
}
}
I am thinking the only other way of doing what I am asking is:
class TestClass {
// Using dummy MyClass that will never be used.
private var myVar: MyClass = new MyClass
private def process(myVar: MyClass) {
this.myVar = myVar
myVar.useVarMethod()
}
}
The Scala way is to declare the variable as Option[MyClass]:
class TestClass {
private var myVar: Option[MyClass] = None
private def createVar() {
myVar = Some(new MyClass)
}
// Usage example:
def useMyVar(): Unit = {
myVar match {
case Some(myClass) => {
// Use myClass here ...
println(myClass.toString)
}
case None => // What to do if myVar is undefined?
}
}
}
That way you avoid NullPointerException. You make it explicit that the variable can be in undefined state. Everytime you use the myVar you have to define what to do if it is undefined.
http://www.scala-lang.org/api/current/index.html#scala.Option
I need myVar to be of type MyClass not Option[MyClass]. I see that I
could use Rado's updated answer and then use the get method, but is
there any other way?
When you use Option you can telling the compiler and everyone else who will read/use your code that it's okay not to define this value and the code will handle that condition and not fail at runtime.
The other way of dealing with is to do null checks every time before you access the variable because it could be null and therefore throw an exception at runtime.
When you use Option, the compiler will tell you if at compile time that you have not handled a condition where the value of a variable maybe undefined.
If you think about it, it's really a big deal. you have converted a runtime exception (which is deterministic) to a compile-time error.
If you want to extract the value out of something like an Option (which supports map and also flatMap), then you don't necessarily have to keep doing pattern matching on whether or not the Option contains a value (i.e. is a "Some") or not (i.e. is a "None").
Two methods are very useful - if you want just alter (or "map") the value within the Option then you can use the map method, which takes a function with a general type of:
f: A => B
so in your case at compile time would end up being:
f: MyClass => B
When you map an option, if the option is a "Some" then the contained value is passed through to the mapping function, and the function is applied (to change the MyClass to a B if you like...) and the result is passed back wrapped in an Option. If your Option is a None, then you just get a None back.
Here's a simple example:
scala> case class MyClass(value : String)
defined class MyClass
scala> val emptyOption : Option[MyClass] = None
emptyOption: Option[MyClass] = None
scala> val nonEmptyOption = Some(new MyClass("Some value"))
nonEmptyOption: Option[MyClass] = Some(MyClass(Some value)
Try and extract the value from both option instances using map:
scala> nonEmptyOption map { s => s.value + " mapped" }
res10: Option[String] = Some(Some value mapped)
scala> emptyOption map { s => s.value }
res11: Option[String] = None
So basically, if you map across an empty option, you always get a None. Map across a non-empty Option and you can extract the contained value and transform it and get the result wrapped back in an Option. Notice that you don't need to explicitly check any patterns...the map method takes care of it.
Flatmap is a bit more challenging to explain, but it basically isn't massively different except that it takes a function which has type:
g: A => M[B]
which basically allows you to take the value out of the Option (the 'A' in the type signature above), do something to it (i.e. a computation) and then return the result ('B') - wrapped in another container such as another Option, List...
The same notion (across Option anyway) that if the Option is a None then nothing really happens still applies. flatMap and map form the basis of Scala "for comprehensions" which you can read about (and are done far more justice than I could!!) in lots of other places.