I started using Scala
few weeks ago. Overall I really like all the features that this language gives to the developer, but it is hard to switch from Java
habits sometimes.
This question is concerning traits. Currently almost everytime I see some part of logic that I would extract to different class in Java
and add constructor parameter to dependent class I am switching to creating trait and mixing it in my class. (if all you have is a hammer, everything looks like a nail)
But I see two problems with my approach.
Testing classes with mixed in traits:
In Java
if I would have class Foo
that would use Bar
and Baz
classes I would probably inject them to my Foo
class and use them. If I do this using traits I will have class Foo extends Bar with Baz
.
Now in my tests I would have to write something like
trait BarMock extends Bar{
override def bar = "barMock"
}
trait BazMock extends Baz{
override def baz = "bazMock"
}
val foo = new Foo with BarMock with BazMock
if I want to swap the implementation of some trait behaviour. Maybe it is just me that is used to writing
Bar bar = mock(Bar.class);
when(bar.bar()).thenReturn("barMock");
Baz baz = mock(Baz.class);
when(baz.baz()).thenReturn("bazMock");
Foo foo = new Foo(bar, baz);
Is it normal to mock traits like I showed in Scala projects? Or I should restrain myself from mixing in mocked traits in tests.
Trait method visibility
This is also connected with my current obsession with traits. In Java
if I inject Bar, Baz
instances to my Foo
instance, I am not automatically adding all public methods from Bar, Baz
to my Foo
interface. If I want to do it, I have to add each delegating method hand by hand. If I am using traits, when I mix in some trait, I am automatically "polluting" Foo
interface with methods from my traits. Is there any way I could achieve something like private inheritance from C++
?
The only solution that comes to my mind is declaring trait methods as protected
and marking the trait as package private. This way methods will be visible in Foo
, but outside of the package someone can't write val bar:Bar = new Foo
. Also if I understand correctly, package private constraint will be validated only when package private trait source code will be in my project, because JVM
has no way of representing the concept of package private and compiler just makes it public in bytecode.