Scala companion with trait vs abstract class from Java code - java

We can define Scala companion object for an abstract class:
object CompanionAbstractClass {
def newInstance():CompanionAbstractClass = CACChild("companion abstract class")
}
//sealed trait TestQ{
sealed abstract class CompanionAbstractClass{
val name:String
}
case class CACChild(name:String) extends CompanionAbstractClass
From Scala code, I can use it as:
val q1 = CompanionAbstractClass.newInstance.name
From Java code, it can be used as:
CompanionAbstractClass.newInstance().name();
For better composition in Scala we prefer to use traits:
object CompanionTrait {
def newInstance():CompanionTrait = CTChild("companion trait")
}
sealed trait CompanionTrait {
val name:String
}
case class CTChild(name:String) extends CompanionTrait
From Scala it can be used in a similar way as previously:
CompanionTrait.newInstance().name
But now from Java I cannot invoke it in the same way as:
CompanionTrait.newInstance().name();
I can do it only via:
CompanionTrait$.MODULE$.newInstance().name();
To improve the previous syntax and get rid of this "$" I can create a wrapper in Scala or in Java (which is not good from my optionion):
object CompanionTraitFactory {
def newInstance():CompanionTrait = CompanionTrait.newInstance()
}
Now from Java I can use it also as:
CompanionTraitFactory.newInstance().name();
Can you please explain, why in case a companion object is defined for a trait, I cannot use it from Java in the ways, how it can be used when it is defined either for abstract class (CompanionAbstractClass case), or even as a usual singleton (CompanionTraitFactory case). Guys, I more or less understand the difference between scala traits and abstract classes. I want to understand, why it was implemented this way and is there a chance that it will be supported in Scala in future.

In the working case, you're calling a static forwarder added to your abstract class. You didn't used to get a static forward on your interface, but you do in 2.12.

Related

Unable to understand Abstraction in Java/Kotlin

I have difficulty understanding the concept of Abstraction. By definition abstraction is the process of hiding certain details and showing only essential information to the user from w3schools.
So below is my class -
open class SimpleClass {
fun engine() {}
}
class ChildSimpleClass : SimpleClass()
abstract class AbstractClass {
abstract fun engine()
}
class AbstractClassImpl : AbstractClass() {
override fun engine() {}
}
interface Interface {
fun engine()
}
class InterfaceImpl : Interface {
override fun engine() {}
}
fun main(){
//first case
val simpleClass: SimpleClass = ChildSimpleClass()
simpleClass.engine()
//second case
val abstractClassImpl:AbstractClass = AbstractClassImpl()
abstractClassImpl.engine()
//third case
val interfaceImpl: Interface = InterfaceImpl()
interfaceImpl.engine()
}
1.Does the first case qualify to be abstraction ?
2.If yes, then why do we say we achieve abstraction using "interfaces and abstract classes" and why not using normal classes ? from here and many other sources which I went through
3.If not, then why does it not qualify to be abstraction as my class only knows about ChildSimpleClass and not about the SimpleClass is this not hiding details about SimpleClass ?
you have multiple choices to do abstraction with. the first choice must be interface because you can implement multiple interfaces but not more than one class. for example, you can have a class that implements both a Driver and an Eater interface. but can't do that with classes. and then you can use an instance of your class as either one.
the second choice must be abstract classes. the difference between an abstract class and an interface is state. abstract classes can have state. and if you need state in your abstraction you should consider using abstract classes. for example a Driver might not need state, but a Car might need it to know if it's started or not(totally depends on your requirements)
the problem with an open class is that you can't force subclasses to implement a method. and also you always need to have implementations for your methods.
also, abstraction is not only about hiding details. it's also about setting rules for your class. for example a class Engine should always have a name and that changes when the type of engine changes. and a start() method that works based on name:
abstarct class Engine {
var isStarted = false
abstract val name: String
fun start() {
isStarted = true
println("engine $name started")
}
}
so as long as you can't define rules with open classes, you should not consider it an abstraction. but as Tenfour04 said in comments, This is just a semantic argument about the word abstraction. but it's important to know the difference.

Extending Kotlin class by Java requires me to reimplement already implemented method

The simplest code to demonstrate the issue is this:
Main interface in Kotlin:
interface Base <T : Any> {
fun go(field: T)
}
Abstract class implementing it and the method:
abstract class Impl : Base<Int> {
override fun go(field: Int) {}
}
Java class:
public class JavaImpl extends Impl {
}
It should work, but it doesn't. The error is
Class 'JavaImpl' must either be declared abstract or implement abstract method 'go(T)' in 'Base'
If the JavaImpl class was in Kotlin, it would work. Also if the T was cast to String or Integer or any object, it would work too. But not with Int.
Is there any clever solution apart from using Integer and suppressing hundreds of warnings in Kotlin subclasses?
Update: created the issue.
Looking at the byte code we can see, that the Impl-class basically has produced the following function:
public go(I)V
where the parameter is a primitive integer. Also a synthetic bridge-function (go(Object)) is generated, which however would also be generated on the Java-side for such generic functions.
On the Java side however it doesn't suffice to have something like public void go(int field) in place. Now we need that go(Integer field)-function, which isn't present.
For me that sound's like an interop-problem that should probably be reported and linked back here again. Actually having had some time to investigate, there seem to be some issues already: KT-17159 and KT-30419, but also KT-5128 seem to relate to this problem. The kotlin compiler knows how to deal with this and doesn't need any further information about it in the class-file (i.e. the implementing Kotlin class knows, that it doesn't need to implement something like fun go(field : Int?)). For the Java-side such counterpart does not exist. I wonder whether this could even be fixed nicely with the compiler/byte-code or whether this will remain a specific interop-problem.
Some workarounds to deal with that problem (in case this is deliberate and not a real problem):
Add an additional function as follows to Impl:
fun go(field : Int?) = go(field ?: error("Actually the given field should never be null"))
// or simply:
fun go(field : Int?) = go(field!!)
That way you would not need to implement it. However then you would also expose that nullable function to the Kotlin side, which you probably don't want.
For that specific purpose it may seem more convenient to do it the other way around:
declare the class and the interface in Java and use it on the Kotlin side. That way you could still declare something like
abstract class KotlinClass : JavaInterface<Int> {
override fun go(field : Int) { // an IDE might suggest you to use Int? here...
// ...
}
}
You can use javap to analyze the problem, showing members of compiled interface and classes.
javap Base
public interface Base<T> {
public abstract void go(T);
}
javap Impl
public abstract class Impl implements Base<java.lang.Integer> {
public void go(int);
public void go(java.lang.Object);
public Impl();
}
So, the problem is exactly that pointed out by #Roland: in order to satisfay the contract requested by the Base interface, the Java compiler needs a public void go(java.lang.Integer) method but the method generated by Kotlin compiler has int as parameter.
If you implement the interface in Java, with something like
class JI implements Base<Integer> {
#Override
public void go(#NotNull Integer field) {
}
}
you can analyze its compiled version with javap obtaining
javap JI
class JI implements Base<java.lang.Integer> {
org.amicofragile.learning.kt.JI();
public void go(java.lang.Integer);
public void go(java.lang.Object);
}
So, if you plan to use Kotlin class Impl as superclass of Java classes, the solution is simply to use <Integer>, not <Int>, as type parameter: Int is a Kotlin class, translated to int by the compiler; Integer is the Java class you usually use in Java code.
Changing your example to
abstract class Impl : Base<Integer> {
override fun go(field: Integer) {}
}
public class JavaImpl extends Impl {
}
the JavaImpl Java class compiles without errors.

How to handle package name changes in Java, if both versions need to be supported?

I need to support two versions of a dependency, which have the same API but different package names.
How do I handle this without maintaining two versions of my code, with the only change being the import statement?
For local variables, I guess I could use reflection (ugly!), but I use the classes in question as method argument. If I don't want to pass around Object instances, what else can I do to abstract from the package name?
Is it maybe possible to apply a self-made interface - which is compatible to the API - to existing instances and pass them around as instance of this interface?
I am mostly actually using xtend for my code, if that changes the answer.
Since you're using Xtend, here's a solution that makes use of Xtend's #Delegate annotation. There might be better solutions that aren't based on Xtend though and this will only work for simple APIs that only consist of interfaces with exactly the same method signatures.
So assuming you have interfaces with exactly the same method signatures in different packages, e.g. like this:
package vendor.api1
interface Greeter {
def void sayHello(String name)
}
package vendor.api2
interface Greeter {
def void sayHello(String name)
}
Then you can combine both into a single interface and only use only this combined interface in your code.
package example.api
interface Greeter extends vendor.api1.Greeter, vendor.api2.Greeter {
}
This is also possible in Java so far but you would have to write a lot boilerplate for each interface method to make it work. In Xtend you can use #Delegate instead to automatically generate everything without having to care how many methods the interface has or what they look like:
package example.internal
import example.api.Greeter
import org.eclipse.xtend.lib.annotations.Delegate
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
#FinalFieldsConstructor
class GreeterImpl implements Greeter {
#Delegate val Api delegate
}
#FinalFieldsConstructor
class Greeter1Wrapper implements Greeter {
#Delegate val vendor.api1.Greeter delegate
}
#FinalFieldsConstructor
class Greeter2Wrapper implements Greeter {
#Delegate val vendor.api2.Greeter delegate
}
Both Greeter1Wrapper and Greeter2Wrapper actually implement the interface of both packages here but since the signature is identical all methods are forwarded to the respective delegate instance. These wrappers are necessary because the delegate of GreeterImpl needs to implement the same interface as GreeterImpl (usually a single delegate would be enough if the packages were the same).
Now you can decide at run-time which version to use.
val vendor.api1.Greeter greeterApi1 = ... // get from vendor API
val vendor.api2.Greeter greeterApi2 = ... // get from vendor API
val apiWrapper = switch version {
case 1: new Greeter1Wrapper(greeterApi1)
case 2: new Greeter2Wrapper(greeterApi2)
}
val example.api.Greeter myGreeter = new GreeterImpl(apiWrapper)
myGreeter.sayHello("world")
This pattern can be repeated for all interfaces. You might be able to avoid even more boilerplate by implementing a custom active annotation processor that generates all of the required classes from a single annotation.

Remove $init$ method from class with subclass

I am using a Java library in Scala that generates D-Bus interfaces from class definitions. It works fine with a simple Scala class:
#DBusInterfaceName("me.TestInterface")
trait TestInterface extends DBusInterface {
def changeSomething(thing: String): Unit
}
I need to add a sub-class in order to implement a D-Bus Signal:
#DBusInterfaceName("me.TestInterface")
trait TestInterface extends DBusInterface {
def changeSomething(thing: String): Unit
final class SomethingChanged(thing: String) extends DBusSignal("SomethingChanged")
}
When I do this I get an extrat method $init$ on my D-Bus interface, presumably because this is being added by Scala.
Is it possible to generate a class with a sub-class in Scala that does not have this extra $init$ method?
I can clearly just write this in Java and import it, but I would rather stick to pure Scala.
I found a solution. I realised that Java uses a static class as the inner class. The equivalent in Scala is to put the class in the companion object:
#DBusInterfaceName("me.TestInterface")
trait TestInterface extends DBusInterface {
def changeSomething(thing: String): Unit
}
object TestInterface {
case class SomethingChanged(thing: String) extends DBusSignal("/me/Test")
}
This does work in Scala 2.13, but I am not sure whether this is behaviour that I can rely on or just a feature of the current implementation.

type definition in a package object "hijacking" the inheritance of a java class in scala code

I have the following situation:
I have a Java class hierarchy like this:
package org.foo.some;
public class Model extends org.foo.some.GenericModel { // ... }
package org.bar;
public class MyModel extends org.foo.some.Model { // ... }
where org.foo.some.Model and org.foo.some.GenericModel are out of my reach (not my code). In Scala, also out of my reach, there is:
package org {
package foo {
package object some {
type Model = org.foo.some.ScalaModel
}
}
}
This leads to a funny behavior in Scala code, e.g.
val javaModel:MyModel = new org.bar.MyModel()
trait FooTrait[T <: org.foo.some.GenericModel] { // ... }
class FooClass extends FooTrait[MyModel] { //... }
does not compile and raises the following error:
type arguments [org.bar.MyModel] do not conform to trait FooTrait's type
parameter bounds [T <: org.foo.some.GenericModel]
Further, I can't invoke any method of org.foo.some.Model nor of org.foo.some.GenericModel on javaModel:
javaModel.doSomething()
raises
value create is not a member of org.bar.MyModel
I am under the impression that the package object is "hijacking" the visibility of the Java class hierarchy in Scala code. Indeed, ScalaModel does not extend org.foo.some.GenericModel.
Is there maybe a way to still access the hierarchy from within Scala code?
Edit: when re-compiling the code out of my reach and removing the type re-definition, everything works. So I think what I'm looking at is a way to "disable" an package-level type definition for a specific class.
Are you using a GUI (in particular Eclipse) to build your project?
This seems related to Scala trouble accessing Java methods (that has no answer but where the general consensus is that the problem is not with scala but with Eclipse).

Categories