In Scala, you can "add new methods" to existing classes by creating wrapper class and using "implicit def" to convert from the original class to the rich wrapper class.
I have a java library for graphics that uses plenty of constructors with looong lists of floats. I would love to add new constructors to these classes with rich wrapping but this doens't seem to work like the above for methods. In other words, I would like to have simpler constructors but still to be able to keep using the original class names and not some wrapper class names but currently I see no other options.
Ideas?
Sounds like you want to use Scala's apply(...) factory methods, which you build in to the companion object for your class.
For example, if you have:
class Foo(val bar: Int, val baz: Int) {
... class definition ...
}
You could add (in the same file):
object Foo {
def apply(bar: Int) = new Foo(bar, 0)
}
With this in hand, creating a new Foo instance, just providing the bar parameter, is as easy as
val myBar = 42
val myFoo = Foo(myBar) // Note the lack of the 'new' keyword.
This will result in myFoo being assigned an instance of Foo where bar = 42, and baz = 0.
Yes, you need a combination of the "Pimp my Library" approach and an apply factory method.
Related
In java and c#,I can write this:
class Tree {
Tree left;
Tree right;
}
but in scala:
class Tree{
val left:Tree
val right:Tree
}
I need to add abstract for class,or write:
val left:Tree=new Tree()
I can write this:
trait Tree{
val left:Tree
val right:Tree
}
but why if I use class,I "have to"and abstract?I don't think it's a good design
Thanks!
The reason you can write
class Tree {
Tree left;
Tree right;
}
in Java and C# is because the fields are initialized to null by default (or to 0, etc. depending on the type). Scala's designers decided this is a bad idea and you need to initialize them. So the approximate Scala equivalent is
class Tree {
// note, not val
var left: Tree = null
var right: Tree = null
}
which is legal (but probably not something you actually want to use).
but why if I use class,I "have to"and abstract?
You have to mark your class with the abstract keyword because your class is abstract. It cannot possibly be instantiated.
I don't think it's a good design
Good design is subjective. The designers of Scala thought that being explicit in this case, rather than making the class automatically abstract was good design. I would guess that the majority of the Scala community agrees with them.
You disagree, and that is perfectly okay.
It happened because scala sometimes considers val and def as method like declarations. Let me elaborate, please, for instance, we have the next Scala example:
class Example {
val a: String = "example"
}
val example = new Example()
println(example.a)
You may see that a declared as a field. Unlike Java, we can change this declaration easily to def and everything remains compiling:
class Example {
def a: String = "example"
}
val example = new Example()
println(example.a)
In the case of Java (not sure about C# I've never worked with it), if you would like to access the field over getter method, you will need to change all invocation places from field to method access.
Now, you can consider val as let's say sort of eager cached def version - if so then declaring val without actually value assignment implicitly considered by compiler as declaring method without implementation and that's why compiler says Tree is an abstract class - because left and right has no values, hence they are abstract. In order to make it non-abstract, you need to assign a value to fields or use var if you would like to proceed with mutable structure, e.g.:
class Example {
val a: String = "example"
}
val example = new Example()
println(example.a)
Scatie: https://scastie.scala-lang.org/bQIcVNk9SN6qJhbL32SMUQ
I want to do something like
class Pack extends collection.immutable.List[Dog]{
def pullSled() = //...
}
But the Scala compiler tells me
illegal inheritance from sealed class List
This would be trivial to do in Java, but I think there is something key I am missing.
Motivations:
I want to be able to use all the base class methods
(new Pack()).contains(snoopy)
I want to be able to extend it, either directly or with mixins
new Pack() with Driver
I would like to be able to change the underlying collection type simply (to switch to a Set for instance)
Thanks!
List[T] is final, hence you cannot extend from it (see why below). If you just need an additional method on a List[Dog], easiest is probably to pimp it:
implicit class Pack(l: List[Dog]) {
def pullSled() = //...
}
If Pack is in scope, the following will work:
val a = List(new Dog)
a.pullSled()
If you really need to create your own collection of dogs, have a look here:
http://www.scala-lang.org/docu/files/collections-api/collections-impl.html
You cannot extend from List[T] because of the following: Suppose you have Pack as you would like to have it, now the following will work, but not really give you what you want:
val pack: Pack = ...
val pack2 = new Dog :: pack
// pack2: List[Dog], not a Pack... :(
Properly extending from LinearSeq as described in the upper link will mitigate this.
General style question.
As I become better at writing functional code, more of my methods are becoming pure functions. I find that lots of my "classes" (in the loose sense of a container of code) are becoming state free. Therefore I make them objects instead of classes as there is no need to instantiate them.
Now in the Java world, having a class full of "static" methods would seem rather odd, and is generally only used for "helper" classes, like you see with Guava and Commons-* and so on.
So my question is, in the Scala world, is having lots of logic inside "objects" and not "classes" quite normal, or is there another preferred idiom.
As you mention in your title, objects are singleton classes, not classes with static methods as you mention in the text of your question.
And there are a few things that make scala objects better than both static AND singletons in java-world, so it is quite "normal" to use them in scala.
For one thing, unlike static methods, object methods are polymorphic, so you can easily inject objects as dependencies:
scala> trait Quack {def quack="quack"}
defined trait Quack
scala> class Duck extends Quack
defined class Duck
scala> object Quacker extends Quack {override def quack="QUAACK"}
defined module Quacker
// MakeItQuack expects something implementing Quack
scala> def MakeItQuack(q: Quack) = q.quack
MakeItQuack: (q: Quack)java.lang.String
// ...it can be a class
scala> MakeItQuack(new Duck)
res0: java.lang.String = quack
// ...or it can be an object
scala> MakeItQuack(Quacker)
res1: java.lang.String = QUAACK
This makes them usable without tight coupling and without promoting global state (which are two of the issues generally attributed to both static methods and singletons).
Then there's the fact that they do away with all the boilerplate that makes singletons so ugly and unidiomatic-looking in java. This is an often overlooked point, in my opinion, and part of what makes singletons so frowned upon in java even when they are stateless and not used as global state.
Also, the boilerplate you have to repeat in all java singletons gives the class two responsibilities: ensuring there's only one instance of itself and doing whatever it's supposed to do. The fact that scala has a declarative way of specifying that something is a singleton relieves the class and the programmer from breaking the single responsibility principle. In scala you know an object is a singleton and you can just reason about what it does.
You can also use package objects e.g. take a look at the scala.math package object here
https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/math/package.scala
Yes, I would say it is normal.
For most of my classes I create a companion object to handle some initialization/validation logic there. For example instead of throwing an exception if validation of parameters fails in a constructor it is possible to return an Option or an Either in the companion objects apply-method:
class X(val x: Int) {
require(x >= 0)
}
// ==>
object X {
def apply(x: Int): Option[X] =
if (x < 0) None else Some(new X(x))
}
class X private (val x: Int)
In the companion object one can add a lot of additional logic, such as a cache for immutable objects.
objects are also good for sending signals between instances if there is no need to also send messages:
object X {
def doSomething(s: String) = ???
}
case class C(s: String)
class A extends Actor {
var calculateLater: String = ""
def receive = {
case X => X.doSomething(s)
case C(s) => calculateLater = s
}
}
Another use case for objects is to reduce the scope of elements:
// traits with lots of members
trait A
trait B
trait C
trait Trait {
def validate(s: String) = {
import validator._
// use logic of validator
}
private object validator extends A with B with C {
// all members of A, B and C are visible here
}
}
class Class extends Trait {
// no unnecessary members and name conflicts here
}
In Scala, if we have
class Foo(bar:String)
We can create a new object but cannot access bar
val foo = new Foo("Hello")
foo.bar // gives error
However, if we declare Foo to be a case class this works:
case class Foo(bar:String)
val foo = Foo("hello")
foo.bar // works
I am forced to make many of my classes as case classes because of this. Otherwise, I have to write boilerplate code for accessing bar:
class Foo(bar:String) {
val getbar = bar
}
So my questions are:
Is there any way to "fix" this without using case classes or boilerplate code?
Is using case classes in this context a good idea? (or what are the disadvantages of case classes?)
I guess the second one deserves a separate question.
Just use val keyword in constructor to make it publicly accessible:
class Foo(val bar:String) {
}
As for your question: if this is the only feature you need, don't use case classes, just write with val.
However, would be great to know why case classes are not good.
In case classes all arguments by default are public, whereas in plain class they're all private. But you may tune this behaviour:
scala> class Foo(val bar:String, baz: String)
defined class Foo
scala> new Foo("public","private")
res0: Foo = Foo#31d5e2
scala> res0.bar
res1: String = public
scala> res0.baz
<console>:10: error: value baz is not a member of Foo
res0.baz
And even like that:
class Foo(private[mypackage] val bar:String) {
// will be visible only to things in `mypackage`
}
For case classes (thanks to #JamesIry):
case class Bar(`public`: String, private val `private`: String)
You can use the BeanProperty annotation to automatically generate java-like getters
import scala.reflect.BeanProperty
case class Foo(#BeanProperty bar:String)
Now Foo has a getBar method that returns the bar value.
Note though that this is only useful if you have a good reason to use java-like getters (typical reasons being that you need your class to be a proper java bean, so as to work with java libraries that expect java beans and use reflection to access the bean's properties).
Otherwise, just access the bar value directly, this is "the scala way".
See http://www.scala-lang.org/api/current/index.html#scala.reflect.BeanProperty
I was about to use case classes with named default parameters as builders. For a simple example:
case class Person(name:String)
case class PersonBob(name:String = "Bob") {
def build = Person(name)
}
val casePerson = PersonBob("case").build
But I could use methods as well:
object Builder {
def personBob(name:String = "Bob"):Person = Person(name)
}
val methodPerson = Builder.personBob("method")
Some tradeoffs:
Method approach eliminates the need to create an object for each builder...saving cycles and memory.
Method approach has a bit leaner implementation code. No advantage for the usage syntax.
Methods can't be passed in as parameters to other builders for "builder composition". Functions can, but we are talking about methods here since functions don't support parameter defaults.
Case class approach allows me to persist builder instances (not needed now).
Case class approach would facilitate building an internal DSL at some point--but I think an external DSL is ambivalent to either approach.
Other considerations? Build a better builder some other way?
Confused am I: isn't the baked-in case class factory all that you need here, or am I missing something. IOW:
scala> case class Foo(bar:String="ab")
defined class Foo
scala> val f = Foo()
f: Foo = Foo(ab)
scala> f.bar
res2: String = ab