So i have simple class named Person, this class have several fields:
class person {
val name:String
val abe:Int
def generateRandom(): Person {
// Here i want to generate Person and return
}
}
So as you can see i want my class will have the option to generate random Person but i don't want to pass any params to my class, i want it to be auto.
So how can i create new Person object inside this generateRandom method and return it with both name and age fields ?
Any suggestions how to implement it ?
You use val for name and age, so there's no other way - you have to set then in constructor. However you can make constructor private and move generator method to companion object (because it can access private constructor). The code would look like this:
class Person private (val name: String, val age: Int)
object Person {
def generateRandom(): Person = {
val randomName = ...
val randomAge = ...
new Person(randomName, randomAge)
}
}
//new Person("asdas", 3) not possible
val person = Person.generateRandom()
println(person.name, person.age)
I think you need to have param: String = null in your parentheses. With the accompanied if(param == null)...
Scala default parameters and null
It seems very simple.
Just return the new Person object from the method, adding some random value to Name and Age.
Related
I am new to Scala and I come from a Java background.
I would like to know the Scala equivalent of this Java code:
public class Person {
String name;
public Person(String name) {
this.name = name.toLowerCase();
}
}
So, when creating Person object, we directly put the lower case version
This is roughly what you want:
class Person private(val name: String)
object Person {
def apply(name: String): Person =
new Person(name.toLowerCase)
}
val p = Person("Tim") // == Person.apply("Tim")
p.name // tim
The private modifier means that you cannot create the class directly using new.
The object has the same name as the class and is the companion object for the class. It has full access to the class, including being able to construct instances using new.
The apply method is used when you "call" an object, and this modifies the parameters before calling the actual constructor.
If I want to modify one single parameter in a constructor.
In the Scala case class, the apply method will be overridden twice. Unless apply applies ( no pun ) to auxiliary constructor.
Related to
Modifying case class constructor parameter before setting value
How to override apply in a case class companion
How one can modify one single input from a constructor ?
Criteria :
The class must hold immutable data. All data must be accessible.
Note it doesn't have to be case class.
Additionally , no need to use the apply method.
no extra unused parameters. In the example below, _fistName is still accessible but unused.
case class Person(
lastName: String,
_fistName: String, ... )
{ lazy val fistName = _fistName.toLowerCase }
Here are two simple approaches.
Using class:
class Person(first: String, last: String) {
val firstName = first.toLowerCase
val lastName = last.toLowerCase()
}
val person = new Person("Adam", "Smith")
println(person.firstName + " " + person.lastName) // adam smith
Using trait + object's apply():
trait Person {
val firstName: String
val lastName: String
}
object Person {
def apply(first: String, last: String) = new Person {
override val firstName: String = first.toLowerCase
override val lastName: String = last.toLowerCase
}
}
val person = Person("Adam", "Smith")
println(person.firstName + " " + person.lastName) // adam smith
Note that classes must be instantiated with new, while traits (created with apply()) don't.
Why no case classes? Well, they are designed to serve as ADTs (abstract data types). Basically, they are thought of as containers for some data without any logic. That's why they get apply() out-of-the-box. If you want to override it, then means your class doesn't have the semantics of a case class.
I can see that #prayag took the effort of trying to help you force it into a case class, but seriously, if it doesn't have to be one (and you said so in the question), then don't make it one.
The reference you had posted seems to have lot of answers as well.
Two simple ways I could think of
make it abstract case class and define companion object which would mutate the value you want
define the member of case class as var and mutate it.
eg. (using scalatest)
class CaseClassSpecs extends FunSpec {
describe("case class modify") {
it("APPROACH 1 : modifies abstract case class member") {
object Item {
def apply(itemId: String, itemName: String) :Item = new Item(itemId.toLowerCase, itemName) {}
}
abstract case class Item private (val itemId: String, itemName: String)
val item1 = Item("SKU-ONE", "Shirts")
assert(item1.itemId == "sku-one")
assert(item1.itemName == "Shirts")
}
it("APPROACH 2 : modifies case class member which is var") {
case class Item (var itemId: String, itemName: String) {
itemId = itemId.toLowerCase()
}
val item1 = Item("SKU-ONE", "Shirts")
assert(item1.itemId == "sku-one")
assert(item1.itemName == "Shirts")
}
}
}
Class parameters are not necessarily class members. You can have class parameters that do not become class members.
Method 1
class Foo(bar0: String) {
val bar = bar0.toLowerCase()
}
#main
def main(): Unit = {
println(Foo("AsDfGh").bar)
}
prints:
asdfgh
and the decompiled code is:
public class Foo {
private final String bar;
public Foo(final String bar0) {
this.bar = bar0.toLowerCase();
}
public String bar() {
return this.bar;
}
}
You see, bar0 is a "temporary" value, it does not become a field because it is not referenced.
So if you want to change a value, just do not use the original value in the methods.
Method 2
For case classes, it does not seem to work in 2022, but here is another trick:
case class Point (x: Double, y: Double)
class PolarPoint(r: Double, alpha: Double) extends Point(r*Math.cos(alpha), r*Math.sin(alpha))
Here r and alpha do not become members of PolarPoint.
If you don't need two types, you can make the 1st constructor protected:
case class Foo protected(x:Int)
class FooImpl(x0:Int) extends Foo(myFunc(x0))
You will reference objects as Foos but create FooImpls.
Method 3
Your class can have multiple parameter lists and implicits:
class Qux(x:String)(implicit val y:String = x.toLowerCase())
is converted to:
public class Qux {
private final String y;
public static String $lessinit$greater$default$2(String var0) {
return Qux$.MODULE$.$lessinit$greater$default$2(var0);
}
public Qux(final String x, final String y) {
this.y = y;
}
public String y() {
return this.y;
}
}
You see that here only y becomes a field.
I am learning the scala and I have try with the following code.
object Demo7 {
def main(args: Array[String]): Unit = {
class Person(val fullName: String) {
println(s"This is the primary constructor. Name is ${fullName}")
val initial = fullName.substring(0, 1) // Computed during initialization
//def this(firstName: String, lastName: String) = this(s"$firstName $lastName")
}
new Person("Tek Tuk")
new Person("Tek Tuk").fullName
}
}
then I run I get the same returned result as each call.
for my understanding this line
new Person("Tek Tuk").fullName
Shouldn't compile, anyone can explain me why this line get compile and return the same result as the first line?
Thank you.
If you're asking why you're allowed to access the fullName field of your Person class, that's because you've declared it as a val in the parameter list.
This is the same as declaring it a public final field in Java. If you want it to be private, just remove the val part, i.e.
class Person(fullName: String) {
(...)
}
As for why both calls "return" the same thing - they don't.
new Person("Tek Tuk") returns an instance of Person.
new Person("Tek Tuk").fullName returns "Tek Tuk" - a String. You created another instance of Person with the same fullName and you called fullName on it.
Both, however, print "This is the primary constructor. Name is Tek Tuk", because you called the same constructor in both cases and you have a println that prints this in the constructor.
class Person(){
val name : String
def this(n : String) {
this()
this.name = n
}
}
compile time error : reassignment to val
i am a newbie to scala and so far i learned how to use primary constructor and case classes for initialization of data members. I am just wandering, if there is a way to initialize val data member inside this. Initialization of var data member works fine below :-
class Person(){
var name : String = _
def this(n : String) {
this()
this.name = n
}
}
You just can't assign to a val after initialization. In Scala the body of the class IS the constructor, you can see examples here.
In general, you just define all variables in the primary constructor itself as "class parameters": class Person(val name: String) if you need to receive the name for initialization or class Person() { val name = 'Joe' } if it is fixed.
This can be quite surprising coming from Java, as you are used to have constructors that produce the values and build the object directly. For such a case, the best solution is to use apply methods on a companion object:
class Person(val name: String)
object Person() {
def apply(db: SomeDBConnectionWrapper, id: Int) = new Person(db.fetchName(id))
}
That allows you to call Person(db, 3) to get a new person with custom initialization, but the constructor itself still receives all it needs to construct a new instance where all values are only assigned once.
In Scala how to put constraints on the fields of a class?
In a package I have the domain of my model, in another package I have dsl to instantiate my model.
The basic form of the model is this:
abstract class Element {
var name: String
var description: String
var types : Set[Type]
}
class SAComponent (var name :String,
var description : String,
var properties : Set[Property] = Set(),
var types : Set[Type] = Set(),
) extends Component
Element is the root of my model.
I want to put constraints on the fields of Element, so that each class that inherits name and description and types of Element respects these constraints.
In other words I need to define the I get for these fields. Right?
How should I do?
I tried that, but the constraints are not respected:
abstract class Element {
def name: String
def name_= (value: String): Unit = {if (isBadValue(value)throw new IllegalArgumentException
name = value
}
var description : String,
var types : Set[Type] = Set }
class Component (override var name : String, var description: String) extends Element
The problem is that some fields that must respect the constraints,
in the constructor of the concrete classes must be initialized to a null value. So the "require" for me is not a good solution.
Thank you all.
Checking at initialization does not work for you because you want stateful objects, which can be avoided by using case classes. Instead of mutating the state of an object, you may want to create new objects using copy(field=value), which is automatically generated for case classes.
If you still want to go with stateful objects, I guess you want something like
abstract class Element {
private var _name: String = null
def name_= (value: String) {
require(!isBadValue(value),"Bad Value")
_name = value
}
def name = _name
def isBadValue(value: String): Boolean
}
class Component (initialName : String) extends Element {
name = initialName
def isBadValue(value: String) = value=="name"
}
val c = new Component(null) // works
c.name = "name" // exception
Another thing to point out: the setter generated by override var name in your code overrides your name_= method, which you may already know.
Abstract val:
trait Init {
val name: String
require(!isBadName(name))
def isBadName(name: String) = true
}
Creation:
new { val name = "init" } with Init