I have trait, this trait is already defined in framework and can not change:
trait GenericProfile {
def firstName: Option[String]
def lastName: Option[String]
def fullName: Option[String]
def email: Option[String]
def avatarUrl: Option[String]
}
I want a class inherit it as:
class BasicProfile(
providerId: String,
userId: String,
firstName: Option[String],
lastName: Option[String],
fullName: Option[String],
email: Option[String],
avatarUrl: Option[String]
) extends GenericProfile{
def providerId=this.providerId //ambiguous reference will be here
...
}
But if I do not re-define the unimplemented method, there is still error since the value in BasicProfile is regarded as private and do not regard it as already implemented.
I understand that it can simply write as override, but I have another class in practice:
case class IdProfile(id:String,
providerId: String,
userId: String,
firstName: Option[String],
lastName: Option[String],
fullName: Option[String],
email: Option[String],
avatarUrl: Option[String])extends BasicProfile(providerId,userId,firstName,lastName, fullName,email, avatarUrl){
}
I do not want the IdProfile override the methods from its parents class BasicProfile, just inherit would be OK.
Since BasicProfile has to make sure all that the defined methods of the trait are implemented (since you don't want to use an abstract class), I'd recommend using a case class for the BasicProfile.
You can extend the BasicProfile with an IdProfile class (not case class) and override the specific methods you are interesed in (or leave them be). If I'm not mistaken that's what your trying to accomplish?
trait GenericProfile {
def firstName: Option[String]
def lastName: Option[String]
def fullName: Option[String]
def email: Option[String]
def avatarUrl: Option[String]
}
case class BasicProfile(
providerId: String,
userId: String,
var firstName: Option[String],
var lastName: Option[String],
var fullName: Option[String],
var email: Option[String],
var avatarUrl: Option[String]
) extends GenericProfile{
}
class IdProfile(id:String,
providerId: String,
userId: String,
firstName: Option[String],
lastName: Option[String],
fullName: Option[String],
email: Option[String],
avatarUrl: Option[String])extends BasicProfile(providerId,userId,firstName,lastName, fullName,email, avatarUrl){
}
If you are trying to stay away from case class I'd recommend taking a look at this Question: Simple Scala getter/setter override
Hope this helps.
To define a readable field in the argument list to a class's constructor, you can use val:
class BasicProfile(
val providerId: String,
val firstName: Option[String],
...
) extends GenericProfile {
...
}
When you do not put val (or alternatively var for a mutable field) on the constructor argument, a field is generally not created.
If you define your class as a case class, then constructor arguments without modifiers are treated as if they have val in front of them, and fields are created for them:
case class BasicProfile(
providerId: String,
...
) extends GenericProfile {
...
}
Related
case class CaseClassJobEvent(
jobId: String,
jobType: Option[String],
inPlanning: Option[Boolean],
teamId: String,
actorId: String,
adminActorId: Option[String],
sessionId: String,
clientSessionId: Option[String],
clientCreatedAt: Long,
seqId: Long,
isSideEffect: Option[Boolean],
opAction: String,
stepId: Option[String],
jobBaseStepId: Option[String],
fieldId: Option[String],
serverReceivedAt: Option[Long]) extends Event
With the trait:
trait Event {
var teamId: String
var actorId: String
}
Produces this error:
class CaseClassJobEvent needs to be abstract, since:
[error] it has 2 unimplemented members.
[error] /** As seen from class CaseClassJobEvent, the missing signatures are as follows.
[error] * For convenience, these are usable as stub implementations.
[error] */
[error] def actorId_=(x$1: String): Unit = ???
[error] def teamId_=(x$1: String): Unit = ???
[error] case class CaseClassJobEvent(
[error] ^
What am I doing wrong? What should I be doing? Is there anyway to use traits or inheritance to enforce properties on a case class? I don't want to enforce methods just properties.
Luis Miguel Mejía Suárez already answered your question, so I thought I would add something here so you might reconsider the design of your class.
Instead of creating a var in your case class, use the .copy method for case class to generate a new object with the changed fields.
So, if you want to change teamId or actorId in your case class do something like this:
val jobEvent = CaseClassJobEvent(...)
val changedJobEvent = jobEvent.copy( teamId = "somenewvalue", actorId = "somenewvalue" )
To answer your original question:
trait Event {
def teamId: String
def actorId: String
}
case class CaseClassJobEvent(
jobId: String,
jobType: Option[String],
inPlanning: Option[Boolean],
var teamId: String,
var actorId: String,
adminActorId: Option[String],
sessionId: String,
clientSessionId: Option[String],
clientCreatedAt: Long,
seqId: Long,
isSideEffect: Option[Boolean],
opAction: String,
stepId: Option[String],
jobBaseStepId: Option[String],
fieldId: Option[String],
serverReceivedAt: Option[Long]) extends Event
A design like this is probably what you wanted. It's fine for a school project, but you would never do something like this in production, especially if you are working in a multithreaded environment. Immutable values are always thread-safe, and you would be breaking that by adding vars to your class.
Just something to consider.
=======
We are currently discussing how to implement CaseClassJobEvent without using mutable values. Here is my suggested implementation.
trait Event {
def teamId: String
def actorId: String
}
case class CaseClassJobEvent(
jobId: String,
jobType: Option[String],
inPlanning: Option[Boolean],
teamId: String,
actorId: String,
adminActorId: Option[String],
sessionId: String,
clientSessionId: Option[String],
clientCreatedAt: Long,
seqId: Long,
isSideEffect: Option[Boolean],
opAction: String,
stepId: Option[String],
jobBaseStepId: Option[String],
fieldId: Option[String],
serverReceivedAt: Option[Long]) extends Event
Everything is the same as the solution you want except teamId and actorId are not vars.
If you need to change the value of teamId and actorId in your case class to something else, do something like this:
def setTeamIdAndActorId(myEvent: CaseClasJobEvent, newTeamId: Option[String], newActorId: Option[String]): CaseClassJobEvent = {
val newEvent1 = if (newTeamId.isDefined) myEvent.copy(teamId = newTeamId.get) else myEvent
val newEvent2 = if (newactorId.isDefined) newEvent1.copy(actorId = newActorId.get) else newEvent1
newEvent2
}
If this seems like a horribly verbose way to have to modify a case class, you're right. What we currently do at our company is use the quicklens library from softwaremill to modify deeply nested case classes with more elegant syntax. It is still not as simple as reassigning a var, but it is more correct in a multithreaded environment, and it is less verbose than calling copy all the time. However, for your purposes, if you want correctness without learning a new library, copy may be your best bet.
=======
The way the conversation is evolving now is that the person who asked the question just wants to have an input that will do something if it has certain fields. This sounds like a job for inheritance.
Suppose I have lots of events.
trait Event
I want to have a function that only does something if my event has actorId and teamId.
trait IdEvent {
def teamId: String
def actorId: String
} extends Event
Here is my function, that only does something if my event is an IdEvent
def processEvent(e: Event): Option[Int] = {
e match {
case event: IdEvent => someProcess(event)
case event: Event => None
}
}
where someProcess has signature
def someProcess(input: IdEvent): Option[Int]
I have a case class:
case class EvaluateAddress(addressFormat: String,
screeningAddressType: String,
value: Option[String]) {
}
This was working fine until I have a new use case where "value" parameter can be a class Object instead of String.
My initial implementation to handle this use case:
case class EvaluateAddress(addressFormat: String,
screeningAddressType: String,
addressId: Option[String],
addressValue: Option[MailingAddress]) {
#JsonProperty("value")
def setAddressId(addressId: String): Unit = {
val this.`addressId` = Option(addressId)
}
def this(addressFormat: String, screeningAddressType: String, addressId: String) = {
this(addressFormat, screeningAddressType, Option(addressId), None)
}
def this(addressFormat: String, screeningAddressType: String, address: MailingAddress) = {
this(addressFormat, screeningAddressType, None, Option(address))
}
}
but I don't feel this is a good approach and it might create some problem in future.
What are the different ways I can accomplish the same?
Edit: Is there a way I can create a class containing three parameters: ** addressFormat, screeningAddressType, value** and handle both the use cases?
You do not need to provide auxilliary constructors here and neither the setter. You could simply use the copy method provided by the case class.
For example:
case class MailingAddress(email:String)
case class EvaluateAddress(addressFormat: String,
screeningAddressType: String,
addressId: Option[String],
addressValue: Option[MailingAddress])
scala> val y = EvaluateAddress("abc", "abc", None, None)
y: EvaluateAddress = EvaluateAddress(abc,abc,None,None)
scala> y.copy(addressId = Some("addressId"))
res0: EvaluateAddress = EvaluateAddress(abc,abc,Some(addressId),None)
You can have a default value for fields in a case class.
So you can have the Optional fields default to None :
case class EvaluateAddress(addressFormat: String,
screeningAddressType: String,
addressId: Option[String] = None,
addressValue: Option[MailingAddress] = None)
Then when you create a new instance of EvaluateAddress, you can choose to pass a value for either of addressId, or addressValue or both ..or nothing at all.
I have a lot of similar case classes which mean different things but have the same argument list.
object User {
case class Create(userName:String, firstName: String, lastName: String)
case class Created(userName:String, firstName: String, lastName: String)
}
object Group {
case class Create(groupName:String, members: Int)
case class Created(groupName:String, members: Int)
}
Given this kind of a setup, I was tired of writing methods that take an argument of type Create and return an argument of type Created. I have tons of test cases that do exactly this kind of thing.
I could write a function to convert one case class into the other. This function converts User.Create into User.Created
def userCreated(create: User.Create) = User.Create.unapply(create).map((User.Created.apply _).tupled).getOrElse(sys.error(s"User creation failed: $create"))
I had to write another such function for Group.
What I'd really like to have is a generic function that takes the two types of the case classes and an object of one case class and converts into the other. Something like,
def transform[A,B](a: A):B
Also, this function shouldn't defeat the purpose of reducing boilerplate. Please feel free to suggest a different signature for the function if that's easier to use.
Shapeless to the rescue!
You can use Shapeless's Generic to create generic representations of case classes, that can then be used to accomplish what you're trying to do. Using LabelledGeneric we can enforce both types and parameter names.
import shapeless._
case class Create(userName: String, firstName: String, lastName: String)
case class Created(userName: String, firstName: String, lastName: String)
case class SortOfCreated(screenName: String, firstName: String, lastName: String)
val c = Create("username", "firstname", "lastname")
val createGen = LabelledGeneric[Create]
val createdGen = LabelledGeneric[Created]
val sortOfCreatedGen = LabelledGeneric[SortOfCreated]
val created: Created = createdGen.from(createGen.to(c))
sortOfCreatedGen.from(createGen.to(c)) // fails to compile
For the record, here is the simplest typesafe syntax I've managed to implement:
implicit class Convert[A, RA](value: A)(implicit ga: Generic.Aux[A, RA]) {
def convertTo[B, RB](gb: Generic.Aux[B, RB])(implicit ev: RA =:= RB) =
gb.from(ga.to(value))
}
And it can be used like this:
case class Create(userName: String, firstName: String, lastName: String)
case class Created(userName: String, firstName: String, lastName: String)
val created = Create("foo", "bar", "baz").convertTo(Generic[Created])
Or the same thing with LabelledGeneric to achieve better type safety:
implicit class Convert[A, RA](value: A)(implicit ga: LabelledGeneric.Aux[A, RA]) {
def convertTo[B, RB](gb: LabelledGeneric.Aux[B, RB])(implicit ev: RA =:= RB) =
gb.from(ga.to(value))
}
val created = Create("foo", "bar", "baz").convertTo(LabelledGeneric[Created]))
I'm creating a new domain object mode. In this model, there are Users and Posts. In the future there will be more models (e.g. Comments). I'm trying to learn how to do this using scala to it's full extent. Here's a naive implementation:
class User(val id: String, val creationDate: DateTime, val name: String, val email: String)
class Post(val id: String, val creationDate: DateTime, val user: User, val title: String, val body: String)
And here's another approach attempting to get rid of the duplicate id and creationDate.
class Model(val id: String, val creationDate: DateTime)
class User(id: String, creationDate: DateTime, val name: String, val email: String) extends Model(id, creationDate)
class Post(id: String, creationDate: DateTime, val user: User, val title: String, val body: String) extends Model(id, creationDate)
I'd like to moderate some of my domain objects. To do this, I'd like to add an isApproved: Boolean field.
class Model(val id: String, val creationDate: DateTime)
class User(id: String, creationDate: DateTime, val name: String, val email: String) extends Model(id, creationDate)
class Post(id: String, creationDate: DateTime, val user: User, val title: String, val body: String) extends Model(id, creationDate)
trait Moderated {
val isApproved: Boolean
}
class ModeratedPost(id: String, creationDate: DateTime, val user: User, val title: String, val body: String, val isApproved: Boolean) extends Post(id, creationDate, user, title, body) with Moderated
I'd also like to prevent bugs in my code by type aliasing user and post Ids.
type Id = String
type UserId = Id
type PostId = Id
class Model(val id: Id, val creationDate: DateTime)
class User(id: UserId, creationDate: DateTime, val name: String, val email: String) extends Model(id, creationDate)
class Post(id: PostId, creationDate: DateTime, val user: User, val title: String, val body: String) extends Model(id, creationDate)
trait Moderated {
val isApproved: Boolean
}
class ModeratedPost(id: String, creationDate: DateTime, val user: User, val title: String, val body: String, val isApproved: Boolean) extends Post(id, creationDate, user, title, body) with Moderated
At this point, I've got several questions.
Where should I define my type aliases? I think they have to be defined inside a class, trait, or object.
My goal in using type aliases for my Ids is to catch errors at compile time. I'd like UserId and PostId to be "subclasses" of Id. I.e. if a method took an Id, I could pass in a PostId. How should I do this?
My Moderated trait does not feel very useful. I still have to declare the isApproved on all classes that mix it in. Any tips here?
Idiomatic scala would go something like:
sealed trait Id { def strVal: String }
case class UserId(strVal: String) extends Id
case class PostId(strVal: String) extends Id
trait Model { def id: Id, def creationDate: DateTime)
case class User(
id: UserId,
creationDate: DateTime,
name: String,
email: String
) extends Model
trait Post extends model {
def id: PostId
def user: User,
def title: String,
def body: String
)
trait Moderated { def isApproved: Boolean }
case class UnmoderatedPost(
id: PostId
creationDate: DateTime,
user: User,
title: String,
body: String,
) extends Post
case class ModeratedPost(
id: PostId,
creationDate: DateTime,
user: User,
title: String,
body: String,
isApproved: Boolean
) extends Post with Moderated
You can define your type aliases in package.scala which can be created for each package.
Lets say you have a simple package org.your.project.
Create a file in directory org/your/project called: package.scala
package org.your.project
package object Types {
type Id = String
type UserId = Id
type PostId = Id
}
Then in the class you wish to use the type aliases add:
import org.your.project.Types._
https://stackoverflow.com/a/3401031/2116622
http://www.artima.com/scalazine/articles/package_objects.html
I'd probably not use types for the reasons you are thinking of.
A type of Id could also be an Int but you made it a String.
Anyone reading the code would have to click around the code base to figure out what the Id really is.
I meet a weird problem in scala. Following is my code, class Employee extends class Person
But this piece of code can not been compiled, I have explicit define firstName and lastName as val variable. Why is that ? Does it mean I have to override val variable in base class ? And what is the purpose ?
class Person( firstName: String, lastName: String) {
}
class Employee(override val firstName: String, override val lastName: String, val depart: String)
extends Person(firstName,lastName){
}
The input parameters for the constructor are not vals unless you say they are. And if they are already, why override them?
class Person(val firstName: String, val lastName: String) {}
class Strange(
override val firstName: String, override val lastName: String
) extends Person("John","Doe") {}
class Employee(fn: String, ln: String, val depart: String) extends Person(fn,ln) {}
If they're not vals and you want to make vals, you don't need to override:
class Person(firstName: String, lastName: String) {}
class Employee(
val firstName: String, val lastName: String, val depart: String
) extends Person(firstName,lastName) {}
Since the constructor arguments have no val/var declaration in Person, and as Person is no case class, the arguments will not be members of class Person, merely constructor arguments. The compiler is telling you essentially: hey, you said, that firstName and lastName are members, which override/redefine something inherited from a base class - but there is nothing as far as I can tell...
class Person(val firstName: String, val lastName: String)
class Employee(fn: String, ln: String, val salary: BigDecimal) extends Person(fn, ln)
You do not need to declare firstName/lastName as overrides here, btw. Simply forwarding the values to the base class' constructor will do the trick.
You might also consider redesigning your super classes as traits as much as possible. Example:
trait Person {
def firstName: String
def lastName: String
}
class Employee(
val firstName: String,
val lastName: String,
val department: String
) extends Person
or even
trait Employee extends Person {
def department: String
}
class SimpleEmployee(
val firstName: String,
val lastName: String,
val department: String
) extends Employee
Unless I've misunderstood your intention, here's how to extend Person.
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) Client VM, Java 1.6.0_21).
Type in expressions to have them evaluated.
Type :help for more information.
scala> class Person( firstName: String, lastName: String)
defined class Person
scala> class Employee(firstName: String, lastName: String, depart: String) extends Person(firstName, lastName)
defined class Employee