I've got some classes defined in java, similar to the code below.
I'm trying to access SomeValue through a derived java class, which is allowed in java, but not in kotlin.
Is there a way to access the field through the derived class?
// java file
// -------------------------------------------------
class MyBaseClass {
public static final class MyInnerClass
{
public static int SomeValue = 42;
}
}
final class MyDerivedClass extends MyBaseClass {
}
// kotlin file
// -------------------------------------------------
val baseAccess = MyBaseClass.MyInnerClass.SomeValue;
// this compiles
val derivedAccess = MyDerivedClass.MyInnerClass.SomeValue;
// ^ compile error: Unresolved reference: MyInnerClass
In Kotlin, nested types and companion objects are not automatically inherited.
This behavior is not specific to Java, you can reproduce the same behavior in Kotlin alone:
open class Base {
class Nested
}
class Derived : Base()
val base = Base.Nested::class // OK
val derived = Derived.Nested::class // Error: 'Nested' unresolved
As such, you explicitly have to qualify the nested class using the base class.
This behavior was deliberately made more strict in Kotlin, to avoid some of the confusion in Java related to accessing static members/classes via derived types. You also see that a lot of IDEs warn you in Java when you use a derived class name to refer to static symbols in the base class.
Regarding terminology, Kotlin has a clear definition of inner classes (namely those annotated with the inner keyword). Not all nested classes are inner classes. See also here.
Related:
Kotlin - accessing companion object members in derived types
Kotlin: How can I create a "static" inheritable function?
Related
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.
I am converting a Java Android project to Kotlin.
I am using API.AI's client, which has two AIConfiguration classes:
Superclass
package ai.api;
public class AIConfiguration implements Cloneable {
public static enum SupportedLanguages {
English("en"),
//...
}
//...
}
Subclass
package ai.api.android;
public class AIConfiguration extends ai.api.AIConfiguration {
public enum RecognitionEngine {
//...
}
In my Java code, I was creating an instance of the subclass, as recommended in the api guide:
final AIConfiguration config = new AIConfiguration("TOKEN",
AIConfiguration.SupportedLanguages.English,
AIConfiguration.RecognitionEngine.System);
Once converted to Kotlin, it looks like this:
val config = AIConfiguration("TOKEN",
AIConfiguration.SupportedLanguages.English,
AIConfiguration.RecognitionEngine.System)
...which causes an Unresolved reference: SupportedLanguages.
I can update the reference to ai.api.AIConfiguration.SupportedLanguages.English, which compiles successfully.
I could import the superclass with import ai.api.AIConfiguration as SuperAIConfiguration and use SuperAIConfiguration.SupportedLanguages, but I would rather reference the enum directly on the subclass.
I don't get it: why is this reference valid in Java but not in Kotlin?
The visibility rules in Kotlin are different from those in Java. Kotlin classes do not "inherit" static nested classes from supertypes, because the rules get too complicated when companion objects come into play. We are trying to keep the rules as simple as possible, and normally there's no issue accessing a nested class through a supertype name, but in your case the short names of the subclass and superclass clash. This is not typical, so you have the options you listed in the question: a fully-qualified name or a rename on import.
For some special use-case I have a small utility to load Java classes from jars using a dynamic class loader DynamicClassLoader. This works fine for Java classes contained in jars. Loading Scala classes from a jar also works without problems. However, instantiating the loaded Scala class leads to the following exception. It looks like the Scala class has private default constructor? Note the compiled Scala class name ending with $
java.lang.IllegalAccessException: Class XXX can not access a member of class ScalaClassYYY$ with modifiers "private"
The snippet below illustrates the idea of what I'm trying to achieve and gives a bit more context. The exception happens at the annotated line:
// deploy and register the new code
byte[] jarBytes = (byte[]) ((Object) message.getAttachment("jar"));
String registerClassName = message.getAttachment("register");
logger.debug("the register is '" + registerClassName + "'");
DynamicClassLoader loader = new DynamicClassLoader(jarBytes);
Class<?> registerClass = loader.lookUp(registerClassName);
// ===> this is where the java.lang.IllegalAccessException happens
IRegisterExecutor registerExecutor = (IRegisterExecutor) registerClass.newInstance();
registerExecutor.register();
Any ideas how to fix?
Obviously, you need to make the default constructor public (it won't work for Java classes without a public default constructor either). E.g.
class ScalaClassYYY() {
...
}
or if you want primary constructor to take some arguments,
class ScalaClassYYY(arg1: Int) {
def this() = this(0)
}
But from
Note the compiled Scala class name ending with $
it seems like you are actually trying to instantiate a Scala object:
object ScalaClassYYY { ... }
In this case, you shouldn't create a new instance and instead use the existing one:
(IRegisterExecutor) registerClass.getField("MODULE$").get(null);
EDIT:
I don't see in your answer how you add a default public constructor to a Scala class that does NOT require any parameters.
A class (not an object) that doesn't require any parameters has a default public constructor already (my first example).
Actually in Java all classes by default offer a public default constructor
No. Only those classes which have no constructors which take arguments.
remove the "(it won't work for Java classes without a public default constructor either)" because it is wrong
The documentation for Class.newInstance() says
IllegalAccessException - if the class or its nullary constructor is not accessible.
So I am pretty sure it's right. If it does work for Java classes without a public default constructor, this seems to be a major bug in the class loader you use. You can test it with a Java class which looks like this:
public class TestClass implements IRegisterExecutor {
public TestClass(int dummy) {}
// some implementation for IRegisterExecutor methods to get it to compile
}
I am trying to instantiate a Java abstract class from my Groovy code. Considering the following Java abstract class (non relevant processing is stripped out from the class):
public abstract class StackOverflow{
public abstract String answerMe();
}
I can easily instantiate it in Groovy this way, and the call to answerMe() will trigger the correct output:
StackOverflow stack = [answerMe : { "Answer" }] as StackOverflow
Now if I modify the StackOverflow class adding a String parameter in the constructor like this :
public abstract class StackOverflowStr{
public StackOverflowStr(String s){}
public abstract String answerMe();
}
I don't really know how to instantiate my object, I tried a lot of thing, but I can't seem to find the right syntax, does someone got any clue ?
You can instantiate it in classic Java style:
StackOverflowStr stack = new StackOverflowStr("javaish"){
String answerMe() {"answer"}
}
Just for the record, and to be clear on wording: in all of these scenarios, you're not instantiating an abstract class.
Abstract classes are classes that can never be instantiated.
You're instantiating a concrete anonymous class that extends an abstract class. B-)
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).