)
I have one Parent class called Mob, and I am creating two subclasses Human and Monster.
abstract class Mob(
var name: String,
var healthPoints: Int,
var attackPower: Int,
var defense: Int
) {
var level: Int = 1
var isAlive: Boolean = true
val attackBooster: Map[String, Int]
def attack(opponent: Mob): Unit
def getSound: String
def boostAttack(attributes: List[Int]): List[Int] = {
attributes.map((x: Int) => x + this.attackPower)
}
}
class Human(
override var name: String,
override var healthPoints: Int,
override var attackPower: Int,
override var defense: Int,
var strength: Int,
var agility: Int,
var intelligence: Int,
var profession: String,
var kingdom: String
) extends Mob(
name: String,
healthPoints: Int,
attackPower: Int,
defense: Int
) {
def this(
name: String,
healthPoints: Int,
attackPower: Int,
defense: Int,
strength: Int,
agility: Int,
intelligence: Int,
profession: String = "",
kingdom: String = ""
) {
this(name, healthPoints, attackPower, defense, strength, agility, intelligence, profession, kingdom)
this.profession = chooseProfession(profession)
this.kingdom = chooseKingdom(kingdom)
}
class Monster(
override var name: String,
override var healthPoints: Int,
override var attackPower: Int,
override var defense: Int,
var toxicity: Int,
var brutality: Int,
var confusion: Int,
var curse: Int,
var kind: String
) extends Mob(
name: String,
healthPoints: Int,
attackPower: Int,
defense: Int
) {
def this(
name: String,
healthPoints: Int,
attackPower: Int,
defense: Int,
toxicity: Int,
brutality: Int,
confusion: Int,
curse: Int,
kind: String = ""
) {
this(name, healthPoints, attackPower, defense, toxicity, brutality, confusion, curse, kind)
this.kind = chooseKind(kind)
}
The problems regarding this inheritance is that:
I cannot overload the default constructor (Constructor is defined twice and ambiguous reference to overloaded definition)
I cannot overload mutable members of the parent Mob class
I am new to Scala, and unfortunately I could not find any solution regarding my issue. Could you please clarify what the problem is and how I can solve it?
You might want to use a companion object with those classes, to overload constructor methods:
Companion Object
Once you have your companion object, you can create as many apply methods as you want. apply in Scala (inside companion objects) is a syntactic sugar that allows you to create a new instance of your class without the new keyword - easier to read constructors (read on when to use companion objects).
Also, as much as you would like to overload the constructor methods with the same parameters, that's just not possible - it's the same constructor to Scala. The compiler wouldn't be able to choose which constructor you need to use, so in order to avoid that situation, Scala does not allow you to use the same constructor twice. If you change the parameters in any way (type or number of parameters), you'll see that it does allow you to create a second constructor.
Related
In the project that I am working on, there is some code that is essentially as follows:
sealed trait Character {
def tags: Seq[String]
def life: Int
// other defs
}
object Character {
def addTag[T <: Character](character: T, tag: String): T = {
val newTags = character.tags :+ tag
// character.copy(tags = newTags) // this doesn't compile
character match {
case c: Person => c.copy(tags = newTags).asInstanceOf[T]
case c: Beast => c.copy(tags = newTags).asInstanceOf[T]
// ten more cases to match each subclass
......
case _ => character
}
}
}
case class Person(title: String,
firstName: String,
lastName: String,
tags: Seq[String],
life: Int,
weapon: String
) extends Character
case class Beast(name: String,
tags: Seq[String],
life: Int,
weight: Int
) extends Character
// ten other case classes that extends Character
......
The code works, but the addTag method doesn't look very pretty for two reasons: first, it uses asInstanceOf; second, it has many lines of case c: ...... each of which are almost the same.
Is there a way to make the code better?
Since the copy method is specific to each case class (takes different parameters) it can't be used from a superclass. What you could do is:
sealed trait Character {
def tags: Seq[String]
def life: Int
// other defs
}
trait Taggable[T <: Character] {
def addTags(t: T, newTags: Seq[String]): T
}
object Character {
def addTag[T <: Character: Taggable](character: T, tag: String): T = {
val newTags = character.tags :+ tag
implicitly[Taggable[T]].addTags(character, newTags)
}
}
case class Person(title: String,
firstName: String,
lastName: String,
tags: Seq[String],
life: Int,
weapon: String
) extends Character
object Person {
implicit val taggable: Taggable[Person] = new Taggable[Person] {
override def addTags(t: Person, newTags: Seq[String]): Person = t.copy(tags = newTags)
}
}
case class Beast(name: String,
tags: Seq[String],
life: Int,
weight: Int
) extends Character
Character.addTag(Person("", "", "", Seq(), 1, ""), "")
// Character.addTag(Beast("", Seq(), 1, 1) // needs implicit as well
This uses the Taggable typeclass that must be implemented by every subclass.
I am trying to map integer to object in Scala. I am new to Scala, so I am facing this error "cannot resolve overloaded constructor Item". Please help in resolving this error.
object Main extends App {
override def main(args: Array[String]): Unit = {
val listMap1: ListMap[Int, Item] = ListMap(1 -> new Item("mobile", 25000, 1) )
}
}
This error I am getting in this part:
new Item("mobile", 25000, 1)
This is my class Item:
class Item (name: String, price: Int, id: Int){
def this(name: String, price: Int, id: Int) = {
this(name,price,id);
}
}
In Scala, the stuff you put inside the parens when describing your class is the constructor. You then overload it with your def this..., and since the have the same signature the compiler can't figure out which one to use.
Note that for situations like this, case class provides a lot of nice syntactic sugar. The best way would be to do something like
case class Item(name: String, price: Int, id: Int)
val map: Map[Int, Item] = Map[Int, Item](1 -> Item("mobile", 25000, 1))
I have started learning Scala just now, help me to understand that how the age is printing where student class has two parameters
class Student(id: Int, name: String) {
var age: Int = 0
def showDetails() {
println(id + " " + name + " " + age)
}
def this(id: Int, name: String, age: Int) {
this(id, name)
this.age = age
}
}
object hi {
def main(args: Array[String]) {
var s = new Student(101, "Sun", 20);
s.showDetails()
}
}
that how the age is printing where student class has two parameters
Student has two constructors. An auxilary constructor with two parameters:
class Student(id: Int, name: String)
But it also defines an additional constructor with three parameters via this():
def this(id: Int, name: String, age: Int)
When you create an instance of Student in main, you use the secondary constructor which accepts three arguments:
var s = new Student(101, "Sun", 20);
Thus, 20 is the age. If you'd use the auxiliary constructor, age would still be set to 0 making showDetails() print out 0.
When you construct this class with two parameters, then the construction process will first call:
var age: Int = 0
setting the age to 0. so the age of the student would be 0 unless you specifically change it.
When you call with three arguments, this is exactly what you do, you construct setting age to 0 and then change it to the third argument.
P.S. you are defining the id and name implicitly to be a private val. It would be better to do it explicitly.
The reason for this is that if you do:
class A(a: Int) {
}
then a is not part of the object at all, a is just an argument for the constructor.
If However you use a in a method:
class A(a: Int) {
def b: Int = a
}
Then scala needs to save it for later use (it must be available outside the construction when b is called). It will therefore transform it to a private val.
To avoid confusion, it would be better to do:
class A(private val a: Int) {
def b: Int = a
}
This question is similar to this one, except that both case class instances need to be accessed with references to their base trait. The reason for this is that more than one case class will be extending the trait, and the exact type won't be known until runtime:
sealed trait baseData {
def weight: Int
def priority: Int
}
sealed trait moreData {
def weight: Int
def priority: Int
def t: String
def id: String
}
case class data1(override val weight: Int, override val priority: Int) extends baseData
case class moreData1 (override val weight:Int, override val priority: Int, override val t: String, override val id: String)extends moreData
val from: baseData = data1(1,2)
val to: moreData = moreData1(3,4,"a","b")
How to write a function with the following signature that copies from into to?
def copyOver[A <:baseData, B <:moreData](from: A, to: B)
I'm sure this is doable with Shapeless, but I'm having trouble since I'm pretty new to it.
I am just dipping my toes into generics and am wondering if there is a better way to achieve the following:
I have a sealed trait that has an abstract name and an overridden equals(). I want the overridden equals to match on both type and name. Below is what I have.
sealed trait NamedCampaign[A <: NamedCampaign] {
def name: String
override def equals(obj: Any): Boolean = obj match {
case x: A => x.name == this.name
case _ => false
}
}
case class AdCampaign(name: String, objective: String, status: String, buyingType: String) extends NamedCampaign[AdCampaign]
case class AdSet(name: String, status: String, dailyBudget: Int, lifetimeBudget: Int, startTime: Int, endTime: Int, campaign: String) extends NamedCampaign[AdSet]
In layman's terms, I want two objects to be considered equal if they are the same class and have the same name. Is there a better/faster/more idiomatic way of doing this?
What you have can't work because of erasure. The type A isn't known at runtime.
Adapted from this related answer:
sealed trait NamedCampaign[A <: NamedCampaign] {
implicit def classTagA: ClassTag[A]
def name: String
override def equals(obj: Any): Boolean = obj match {
case classTagA(x) => x.name == this.name
case _ => false
}
}
case class AdCampaign(name: String, objective: String, status: String,
buyingType: String)(implicit val classTagA: ClassTag[AdCampaign])
extends NamedCampaign[AdCampaign]
case class AdSet(name: String, status: String, dailyBudget: Int,
lifetimeBudget: Int, startTime: Int, endTime: Int, campaign: String)
(implicit val classTagA: ClassTag[AdSet]) extends NamedCampaign[AdSet]
A better way to write this is with a canEqual method.
sealed trait NamedCampaign {
def name: String
def canEqual(that: Any): Boolean
override def equals(other: Any): Boolean = other match {
case that: NamedCampaign => (that canEqual this) &&
(this.name == that.name)
case _ => false
}
}
case class AdCampaign(name: String, objective: String, status: String,
buyingType: String) extends NamedCampaign {
override def canEqual(that: Any) = that.isInstanceOf[AdCampaign]
}
case class AdSet(name: String, status: String, dailyBudget: Int,
lifetimeBudget: Int, startTime: Int, endTime: Int, campaign: String)
extends NamedCampaign {
override def canEqual(that: Any) = that.isInstanceOf[AdSet]
}
My two cents: I don't think it's ever appropriate to override equals on a case class. You'll regret it the moment you ever want to compare all of the fields (which you're likely to want to do in, say, a unit test).