In my Scala project, I have the following class hierarchy:
class A {
def methodA = ...
... other methods here ...
}
class B extends A {
... other methods here ...
}
In my class design, it makes perfect sense to make B a subclass of A. The only problem is that methodA is only applicable to class A but not to class B. Unfortunately, I cannot set methodA to private because it needs to be callable from any instance of class A.
Do I have to rethink my class design or is it possible to restrict the access to public method methodA to class A?
In my class design, it makes perfect sense to make B a subclass of A. The only problem is that methodA is only applicable to class A but not to class B.
These statements can't be true at the same time.
Unfortunately, I cannot set methodA to private because it needs to be callable from any instance of class A.
Every instance of B is an instance of A (that's just what "subclass" means). So if the method isn't callable from an instance of B, it isn't "callable from any instance of class A".
One of possible way below:
class A protected() {
//.... methods ....
}
trait AExt extends A {
def foo(s : String) = println(s)
}
object A {
def apply() : A with AExt = new A with AExt
}
class B extends A {
//.... methods ....
}
val a = A()
a.foo("hello")
val b = new B()
// b.foo("hello") // no 'foo' method
Related
Taking the following class and interface:
public class A {
public int foo() {
return 1;
}
}
public interface B {
default int foo() {
return 2;
}
}
If you define a sublclass in java like that
public class C extends A implements B {
}
Calling foo on an instance of C will return 1, as java prefers the superclass over the interface.
Doing the same thing in scala gives a compile error:
class D extends A with B
Error:(3, 9) class D inherits conflicting members:
method foo in class A of type ()Int and
method foo in trait B of type ()Int
(Note: this can be resolved by declaring an override in class D.)
Manually overriding the method
class D extends A with B {
override def foo(): Int = super.foo()
}
Still gives incorrect behaviour, calling foo on an instance of D will return 2.
To get a scala class to behave the same way as java in this case you have manually override every single method and specify A as the superclass in every call.
class D extends A with B {
override def foo(): Int = super[A].foo()
}
Why was it implemented like that? And is there a reasonable way to work around it, without manually overriding every method in that situation?
I cannot figure out how to specify the return type of a method an abstract class A, if the same method of the concrete classes have different return (sub)types:
abstract class A {
def method: List[Common (?)] // I want to force all subclasses to define this method
}
class B1 extends A {
def method(a,b,c): List[Sub1] = {...}
}
class B2 extends A {
def method(a,b,c): List[Sub2] = {...}
}
I tried to define a common trait of Sub1 and Sub2:
abstract class Common // or abstract class
case class Sub1 extends Common
case class Sub2 extends Common
but I keep getting this:
Compilation error[class B1 needs to be abstract,
since method "method" in class A of type => List[Common] is not defined]
If I don't define the return type in class A, I get the same error with ... type => Unit ... instead.
How can I solve that?
def method: List[Common]
Is not the same as
// returns `List[Common]` to simplify things, but it would be the same if we returned a sub-type
def method(a: ?, b: ?, c: ?): List[Common] = {...}
The first is a parameterless method that returns a List[Common], and the second is a method with three parameters that returns a List[Common]. The compiler sees these as two completely different methods. The fact that they have the same name means nothing.
The compiler is complaining because def method: List[Common] is not defined in the subclasses of A.
This compiles:
abstract class Common // or abstract class
case class Sub1() extends Common
case class Sub2() extends Common
abstract class A {
def method(): List[Common]
}
class B1 extends A {
def method(): List[Sub1] = ???
}
class B2 extends A {
def method(): List[Sub2] = ???
}
All I have done is:
add the () to Sub1() and Sub2()
return List[Common] from A
EDIT
As #m-z mentioned, it works because every def method() has the same signature now.
I have 3rd party class A:
class A {
def methodA = ...
}
I want to use use trait to add a new method methodT to an instance of A
trait Atrait[...] {
def methodT = {
// how to get a reference of instance of type A?
}
}
This methodT is specific to some situation, so I should use constraint in the trait. But I could not figure it out. Also, how can I invoke instance of A's method in a trait?
UPDATE
Trait doesn't work this way. See answer for alternative solution.
This is the standard pattern for adding a method to a 3rd party class:
class A
implicit class ExtendedA(val a: A) extends AnyVal {
def methodT: Unit = { println("called A.methodT") }
}
Then you can do:
val a = new A
a.methodT
Why if I have:
trait T {
def method(a: Int)
}
class A extends T {
//...
}
class B extends A {
//...
}
then when I do this:
//...
val b = new B
b.method(15)
//...
the method() is said to be undefined for B? Why do I have to explicitly say that
class B extends A with T
in order to obtain what I want? Are not traits of parent classes inherited? How can it be so if they may realize a big part of parent's own methods which are inherited by definition? If it is so, what is the argument?
I think you just did not implement the method method because I tested it on my computer and the following code works:
scala> trait T {
| def method(a:Int) =a
| }
defined trait T
scala> class A extends T
defined class A
scala> class B extends A
defined class B
scala> val b = new B
b: B = B#164a40a0
scala> b.method(11)
res25: Int = 11
Your code does not compile, because the method is never implemented. B cannot be instantiated because the classes are all abstract.
Add the method body like this, inside Trait A:
def method(a: Int)={
//do something useful here
}
It then compiles, and there are no errors, and indeed, the instance of B may use the method.
I have some classes with a protected constructor and the factory method is inside the companion object of an abstract super class. As of Scala 2.9.0.RC4 this doesn't compile anymore. I have "fixed" the issue by making the constructors package protected. But I don't want other classes even inside the same package to be able to call the constructors.
So what should I?
sealed abstract class A
object A {
//the factory method, returning either a B or C
def apply(): A
}
class B protected (...) extends A
class C protected (...) extends A
You could make them private inner classes of the object.
object A {
private class B extends A
private class C extends A
}
Since you need the classes accessible for pattern matching, I would suggest creating a new subpackage for them and making the constructor private to that package. Now only the import statements in your client code need to be changed.
sealed abstract class A {
}
package myPackage.subPackage {
object A {
def apply(): A = new B
}
class B private[subPackage] () extends A {
}
}
package other {
object Foo {
def foo {
myPackage.subPackage.A()
//does not compile: new myPackage.subPackage.B
}
}
}
Another option is to create companion objects for each implementation of A and delegate construction to a factory method in this object:
sealed abstract class A
object A {
//the factory method, returning either a B or C
def apply(): A = {
if (...) B()
else C()
}
}
object B {
def apply() : B = new B()
}
class B private (...) extends A
object C {
def apply() : C = new C()
}
class C private (...) extends A