My question is about scala inheritance details. I have the following code.
package scalasandbox
object Main {
def main(args: Array[String]): Unit = {
val creature: Creature = new Human("First")
creature.rename("Second")
creature.introduce
}
}
class Creature(var name: String) {
def introduce = println("I'm creature: " + name)
def rename(newName: String) = {
println("Creature was renamed to: " + newName)
name = newName
}
}
class Human(name: String) extends Creature(name) {
override def introduce = println("I'm Human: " + name)
}
which produces the following output
Creature was renamed to: Second
I'm human: First
I expect it to be "I'm human: Second" because rename method should change the field value. I have opened Human class with decompiler:
package scalasandbox;
import scala.Predef.;
import scala.ScalaObject;
import scala.collection.mutable.StringBuilder;
import scala.reflect.ScalaSignature;
#ScalaSignature(bytes="\006\001\0212A!\001\002\001\013\t)\001*^7b]*\t1!\001\007tG\006d\027m]1oI\n|\007p\001\001\024\007\0011!\002\005\002\b\0215\t!!\003\002\n\005\tA1I]3biV\024X\r\005\002\f\0355\tABC\001\016\003\025\0318-\0317b\023\tyABA\006TG\006d\027m\0242kK\016$\b\002C\t\001\005\003\005\013\021\002\n\002\t9\fW.\032\t\003'Yq!a\003\013\n\005Ua\021A\002)sK\022,g-\003\002\0301\t11\013\036:j]\036T!!\006\007\t\013i\001A\021A\016\002\rqJg.\033;?)\taR\004\005\002\b\001!)\021#\007a\001%!)q\004\001C!A\005I\021N\034;s_\022,8-Z\013\002CA\0211BI\005\003G1\021A!\0268ji\002")
public class Human extends Creature
implements ScalaObject
{
private final String name;
public void introduce()
{
Predef..MODULE$.println(new StringBuilder().append("I'm Human: ").append(this.name).toString());
}
public Human(String name)
{
super(name);
}
}
and see "private final String name;" there. I think it hides Creature name field. And
Predef..MODULE$.println(new StringBuilder().append("I'm Human: ").append(this.name).toString());
this stuff looks also suspicious because of "this.name" instead of method call "this.name()". Could anyone explain where is my mistake and what is the correct way of implementing those two classes?
The name variable that you use in the Human class resolves to the constructor parameter of Human, and Scala will automatically create private vals for constructor parameters that you use outside the constructor. This is unfortunate in your case. You can prevent this e.g. by naming your parameter in Human differently, e.g. nm:
class Human(nm: String) extends Creature(nm)
try by changing this line:
class Human(foo: String) extends Creature(foo) {
so you don't hide name.
If Creature is only intended for subclassing, I'd advise that you make both the class and the name parameter abstract, as per my answer here:
Idiomatic Scala way to deal with base vs derived class field names?
One advantage to such a technique is that it then becomes far easier to use case classes for the specific, concrete, sub-classes
Related
I know this question has been asked before here. But the answers there do not satisfy my doubt.
I was told that they prevent mix-up of class types, the code below shows that they're not mixed up at all.
So, it shouldn't matter right?
Classes:
package Practice
abstract class Animal {
def name: String
}
abstract class Pet extends Animal {}
class Cat extends Pet {
override def name: String = "Cat"
}
class Dog extends Pet {
override def name: String = "Dog"
}
Here is the real confusion:
//Class with Upper Bound
class PetContainer[P <: Pet](p: P) {
def pet: P = p
}
//Class with Subtyping(Or Upcasting, I think they're the same)
class SimplePetContainer(p: Pet){
def pet: Pet = p
}
Driver Code:
val CatContainer: PetContainer[Cat] = new PetContainer[Cat](new Cat)
val DogContainer: SimplePetContainer = new SimplePetContainer(new Dog
println(CatContainer.pet.getClass)
println(DogContainer.pet.getClass)
Output:
class Practice.Cat
class Practice.Dog
//Practice was the package
Like I mentioned before, the classes are preserved.
So my question is, What advantage does Upper Bound have on Subtyping?
With your CatContainer, you know that CatContainer.pet is a Cat at compile-time. Meaning that the compiler also knows that. So you can say
CatContainer.pet.meow()
For the SimplePetContainer you do not have static type information about the pet inside anymore.
Like I mentioned before, the classes are preserved.
At runtime, the pet of course still knows its type (well, almost, it knows its class, which in your case would have been enough, any extra type information such as the generic types of that class has been erased).
But the variable DogContainer.pet lacks information about what sort of Pet it contains.
I was told that they prevent mix-up of class types
The compiler won't stop you from writing
val DogContainer = new SimplePetContainer(new Cat())
but it will reject this
val DogContainer = new PetContainer[Dog](new Cat())
Scala throws "reassignment to val" error for the following code.
abstract case class Gun(var bulletCount:Int)
class Pistol(bulletCount:Int) extends Gun(bulletCount){
def fire() { bulletCount=bulletCount-1 }
}
Anything I missed here?
For starters, you should consider case class as final, and not extend them.
Second, do not use var with case class, you should rather create a copy of a case class to get one of its field changed.
Third, if you want a common type, you can use a base trait.
All in one, here's what it could look like:
sealed trait Gun {
def bulletCount: Int
}
case class Pistol(bulletCount: Int) extends Gun {
def fire(): Pistol = copy(bulletCount=bulletCount)
}
You're referring to bulletCount field generated by Pistol primary constructor parameter. To set base class variable, you need to directly call field using super:
class Pistol(bulletCount: Int) extends Gun(bulletCount) {
def fire(): Unit = {
super.bulletCount = super.bulletCount - 1
}
}
Alternatively, you can label parameter-generated field with override var:
class Pistol(override var bulletCount: Int) extends Gun(bulletCount) {
def fire(): Unit = {
bulletCount = bulletCount - 1
}
}
On a side note, as Frederic A. suggested in his answer, you should avoid inheriting case classes. They are syntactic sugar, and code generation don't work over inheritance - you'll need to implement all the fancy stuff like apply or unapply methods in companion class all by yourself. Scala compiler team tried to support case class to case class inheritance, but discovered that it breaks structural equality and lots of other things.
I have a trait that's implemented by a large number of classes, and I'd like to use the names of the classes that implement this trait at runtime, but with as much code centralized as possible.
Specifically, in my code, I'm using tokens to represent classes to be initialized at runtime. The tokens carry configuration, and the actual class is instantiated as needed via the token, combined with run-time information. For linking with resources outside of my app, I want to be able to access the name of the class for which a token is defined. See the example:
trait Token[Cls] {
val className = ???
// Example generic method depending on final class name
def printClassName = println(className)
}
case class ClassA(t: ClassAToken, runtimeContext: String) {
// a bunch of other code
}
object ClassA {
case class ClassAToken(configParam: String) extends Token[ClassA]
}
So, I'm trying to implement className. Ideally, I can pull this information once at compile time. How can I do this, while keeping boilerplate code out of ClassA? Although, if I can drop the type parameter and get the name of the class implementing the Token trait at runtime, that's great too.
Due to Type Erasure Cls is not available on runtime anymore. To get the informations at runtime, you need to use a TypeTag (in your case a ClassTag).
Your code could look like this:
import scala.reflect._
trait Token[Cls] {
def className(implicit ct: ClassTag[Cls]) = ct.runtimeClass.getName
// Example generic method depending on final class name
def printClassName(implicit ct: ClassTag[Cls]) = println(className)
}
case class ClassA(t: ClassAToken, runtimeContext: String) {
// a bunch of other code
}
object ClassA {
case class ClassAToken(configParam: String) extends Token[ClassA]
}
or if it is possible for you to let Token be an class, you could use the ClassTag context bounds:
import scala.reflect._
class Token[Cls: ClassTag] {
def className = classTag[Cls].runtimeClass.getName
// Example generic method depending on final class name
def printClassName = println(className)
}
case class ClassA(t: ClassAToken, runtimeContext: String) {
// a bunch of other code
}
object ClassA {
case class ClassAToken(configParam: String) extends Token[ClassA]
}
For more informations on TypeTags/ClassTags see Scala: What is a TypeTag and how do I use it?
Could someone explain why scala would allow a public variable, to satisfy the implementation of an abstract declared Protected item? My first assumption is that the compiler would complain, but I created a small test to see if this worked, and to my surprise it does. Is there an advantage to this? (perhaps this is normal in OOP?) Any methods to avoid the accidental pitfall?
object NameConflict extends App {
abstract class A {
protected[this] var name:String
def speak = println(name)
}
class B(var name:String) extends A { //notice we've declared a public var
}
val t = new B("Tim")
t.speak
println(t.name) // name is exposed now?
}
It's normal and as in Java. Sometimes it's desirable to increase the visibility of a member.
You can't do it the other way around and turn down visibility in a subclass, because the member can by definition be accessed through the supertype.
If invoking a method has terrible consequences, keep the method private and use a template method that can be overridden; the default implementation would invoke the dangerous method.
abstract class A {
private[this] def dangerous = ???
final protected def process: Int = {
dangerous
template
}
protected def template: Int = ???
}
class B extends A {
override def template = 5
}
I am writing my first large Scala program. In the Java equivalent, I have an enum that contains labels and tooltips for my UI controls:
public enum ControlText {
CANCEL_BUTTON("Cancel", "Cancel the changes and dismiss the dialog"),
OK_BUTTON("OK", "Save the changes and dismiss the dialog"),
// ...
;
private final String controlText;
private final String toolTipText;
ControlText(String controlText, String toolTipText) {
this.controlText = controlText;
this.toolTipText = toolTipText;
}
public String getControlText() { return controlText; }
public String getToolTipText() { return toolTipText; }
}
Never mind the wisdom of using enums for this. There are other places that I want to do similar things.
How can I do this in Scala using scala.Enumeration? The Enumeration.Value class takes only one String as a parameter. Do I need to subclass it?
Thanks.
You could do this which matches how enums are used:
sealed abstract class ControlTextBase
case class ControlText(controlText: String, toolTipText: String)
object OkButton extends ControlText("OK", "Save changes and dismiss")
object CancelButton extends ControlText("Cancel", "Bail!")
I'd like to propose the following workaround for the issue:
object ControlText extends Enumeration {
type ControlText = ControlTextValue
case class ControlTextValue(controlText: String, toolTipText: String) extends Val(controlText)
val CANCEL_BUTTON = ControlTextInternalValue("Cancel", "Cancel the changes and dismiss the dialog")
val OK_BUTTON = ControlTextInternalValue("OK", "Save the changes and dismiss the dialog")
protected final def ControlTextInternalValue(controlText: String, toolTipText: String): ControlTextValue = {
ControlTextValue(controlText, toolTipText)
}
}
Now you can use ControlText as Java enum:
val c: ControlText
c.toolTipText
The only a little bad smell is to get enum object by withName or apply methods. You have to do a cast:
val c: ControlText = ControlText.withName(name).asInstanceOf[ControlText]
Following on from Mitch's answer, if you find that the sealed behaviour is not restrictive enough in limiting subclassed instances to the file where the base class is defined, you can use an object (module) definition like this:
object ControlTexts {
sealed abstract class ControlTextBase
case class ControlText private[ControlTexts] (controlText: String,
toolTipText: String)
extends ControlTextBase
object OkButton extends ControlText("OK", "Save changes and dismiss")
object CancelButton extends ControlText("Cancel", "Bail!")
}
which obviously limits further instantiation of ControlText instances. The sealed keyword is still important in helping detect missing cases in pattern matching.