Reputation: 1497
I have just started learning scala and have some issues with general understanding of type parameters. Previously I learned Haskell, but in scala it seems very confusing to me. It is unclear for me where should I put type parameters (near class definition or function definition).
Consider the example:
class Person[A] {
def sayName (name: A) = println(name)
}
Does it make sense to move type parameter from class to function?
class Person {
def sayName[A] (name: A) = println(name)
}
Or I even can leave type parameter both in class and function and it will work. Does it make some big difference? Will the [A] parameters in function override same param from class definition?
And I can create the instance of Person or call a function without pointing the exact type.
val p = new Person();
So why is this for? Just in cases when I want smth generic? So it is unclear for me when and on which positions (class or function) should I put type parameters.
Upvotes: 4
Views: 6419
Reputation: 37852
If you declare the type parameter at the class level, you assign the actual type upon construction, and can't change it later - all invocations of sayName
on a given instance will have to use the same type.
If you declare the type parameter at the method level, you can assign a different type in each invocation of the method.
So - if an instance of the class should always apply to a single type - use class-level definition.
For example:
trait Animal
case class Dog() extends Animal
case class Cat() extends Animal
// A single owner has a *specific* pet,
// so it makes sense to declare type at class level
class Owner[A <: Animal] {
def feed(a: A) = ???
}
// A single RandomAnimalLover walking down the street,
// might feed both Dogs and Cats - so their feed method must be parameterized, and there's no use in adding a parameter at the class level
class RandomAnimalLover {
def feed[A <: Animal](a: A) = ???
}
val dog = Dog()
val cat = Cat()
val ownerA = new Owner[Dog]
val randomDude = new RandomAnimalLover
ownerA.feed(dog) // compiles
ownerA.feed(cat) // does not compile
randomDude.feed(dog) // compiles
randomDude.feed(cat) // compiles
Lastly, if you declare a type parameter both at the class level and at the method level - these are two separate parameters!
Upvotes: 9
Reputation: 475
What you want to achieve will dictate where to put the type parameter.
Put the type parameter in the class definition to make some attribute of your class generic. It could be a val
, a class parameter or a function argument.
Put the type parameter in the class definition to allow your method to be called with a generic type.
class Person[A](name: A) {
def sayName = println(name)
def sayFirstName(firstName: A) = println(firstName)
def saySomething[B](thing: B) = println(s"$name says $thing")
}
// in console
scala> val p = new Person[String]("Barker")
p: Person[String] = Person@4f5a80a8
scala> p.sayName
Barker
scala> p.sayFirstName("John")
John
scala> p.sayFirstName(4)
<console>:14: error: type mismatch;
found : Int(4)
required: String
p.sayFirstName(4)
^
scala> p.saySomething("hello")
Barker says hello
scala> p.saySomething(42)
Barker says 42
Method sayFirstName
takes an argument the same type as name
. I want name
and firstName
to have the same type, and I can enforce it this way.
Method saySomething
has a new type parameter B
. So the Person
can say something of any type.
As you noted, you could also replace B
by A
and have the same behavior: the type argument in the method definition will override the one in the class definition. But this should be avoided for the sake of readability.
Upvotes: 1