orrsella
orrsella

Reputation: 307

Scala Extending Parameterized Abstract Class

I'm fairly new to Scala, and am writing an API library and trying to use only vals and immutable objects (where possible). I'm trying to decide on the right way to model the objects. Suppose there are several "Post" objects, which all share some common fields, and each adds its own specific fields:

abstract class Post(val id: Int, val name: String, ...)
case class TextPost(val id: Int, val name: String, ..., val text: String) extends Post(id, name, ...)
case class PhotoPost(val id: Int, val name: String, ..., val url: String) extends Post(id, name, ...)

This method adds a lot of repetitive declarations and boiler-plate code, especially when dealing with many Post subclasses and shared fields declare in the abstract class. The way I see it, this is caused by the use of vals which can only be initialized by the constructor. Is there a better way to create the relationships detailed here, or must I suffer this for wanting to use only immutable objects?

Upvotes: 13

Views: 10959

Answers (1)

0__
0__

Reputation: 67330

First of all, the val modifiers in the case-class constructors are redundant, the Scala compiler will give them to you already "for free" (it's one of the features of case-classes). If you actually tried to compile that, you will see that an error occurs because of undeclared overriding:

abstract class Post(val id: Int, val name: String )
case class TextPost(id: Int, name: String) extends Post(id, name )
case class PhotoPost(id: Int, name: String) extends Post(id, name )

<console>:10: error: overriding value id in class Post of type Int;
 value id needs `override' modifier
           case class PhotoPost(id: Int, name: String) extends Post(id, name )
                                ^

I personally recommend to use abstract classes with constructor arguments as little as possible. Scala has traits which do a better job here. Instead of constructor arguments, you declare (abstractly) the fields; then the case classes kick in and due to them making their arguments automatically vals, you are done:

trait Post { def id: Int; def name: String }
case class TextPost(id: Int, name: String, text: String) extends Post
case class PhotoPost(id: Int, name: String, url: String) extends Post

Furthermore, if you add sealed to the trait Post, you can safely use the subtypes of Post in pattern matching without accidentally missing cases.

Upvotes: 14

Related Questions