If I create an anonymous class in Kotlin like this:
if(condition) {
object: Foo() {
fun bar() {
// code
}
}
} else {
Foo()
}
Is there a way to check in the code that the current instance has is the object class and hence I can call bar() which does not exist in Foo?
That object expression is creating an anonymous class, like m.antkowicz has pointed out:
Note that anonymous objects can be used as types only in local and private declarations. If you use an anonymous object as a return type of a public function or the type of a public property, the actual type of that function or property will be the declared supertype of the anonymous object, or Any if you didn't declare any supertype. Members added in the anonymous object will not be accessible.
That means your if expression is returning a local type which is known to contain bar(), but once it leaves that scope it will be declared as the supertype you used - it will be a Foo which does not contain a bar() method.
So, you need to use a supertype that does contain that member - you can use an interface to this, just like when you define a (non-anonymous) class:
interface Bar {
fun bar()
}
object : Foo(), Bar {
override fun bar() {...}
}
then you can use is Foo and is Bar to check which types the object has.
If you want to arbitrarily add functions to objects outside of the type system, and have other code able to know those functions are there, you're probably looking at doing reflection
Related
I'm trying to use inline classes in Kotlin to create a class inlining the String class, such that if I have an instance of my class that it will always be true for the contained string that s == s.trim().
I was initially expecting there to be a straightforward way to do this, like perhaps:
#JvmInline
value class Trimmed private constructor(val str: String) : {
constructor(s : String) : super(s.trim())
}
but that doesn't work, and neither do the other direct approaches I considered ("this(s.trim())", etc.).
This problem has turned out to be surprisingly tricky:
Kotlin seems to provide no easy way to have the primary constructor filter or modify the data that is passed to the constructor of the contained String object.
Even if I make the primary constructor private, I can't declare another constructor with the same signature (taking a single String as a parameter).
If this were a normal (non-inlined) class, I could just set the value after superclass class construction (e.g. "init { str = str.trim() }", but since it's an inline class, I can't do that. ("this=this.trim()" doesn't work either, and String objects themselves are immutable so I can't change the contents of 'str'.)
I tried making the class constructor private and creating a factory function in the same file with the same name as the class, but then I couldn't call the class constructor from within the factory function due to access restrictions.
I then tried making the factory function within the class's companion object, but then Kotlin tried to make that function call itself recursively instead of calling the class's constructor. I wasn't able to find a way to syntactially disambiguate this. I managed to work around this by creating a file-private typealias to give another name for the class so I could call the constructor from within the factory function. (Annoyingly, I couldn't declare the typealias in the companion object next to the factory function: I had to declare it outside.)
This worked, but seemed ugly:
typealias Trimmed2 = Trimmed
#JvmInline
value class Trimmed private constructor(val str: String) {
init { assert(str == str.trim()) }
companion object {
// Kotlin won't let me put the typealias here. :-(
fun Trimmed(s: String): Trimmed = Trimmed2(s.trim()) // Don't want recursion here!
}
}
Another working solution is here, using a private constructor with a dummy argument. Of course Kotlin complained that the dummy argument was unused and so I had to put in a big (why is it so big?) annotation suppressing the warning, which is, again, ugly:
#JvmInline
value class Trimmed private constructor(val str: String) {
private constructor (untrimmed: String, #Suppress("UNUSED_PARAMETER") dummy: Unit) : this(untrimmed.trim())
init { assert(str == str.trim()) }
companion object {
fun Trimmed(s: String): Trimmed = Trimmed(s, Unit)
}
}
Is there a simpler, cleaner way to do this? For instance, a syntactic way to clarify to Kotlin that the companion function is trying to call the class constructor and not itself and so avoid the need for a dummy parameter?
Goals:
Code to construct instances of the class from outside this file should look like constructing an instance of a normal class: 'Trimmed("abc")', not using some factory function with a different name (e.g. "of" or "trimmedOf") or other alternate syntax.
It should be impossible to construct the object containing an untrimmed string. Outside code, and the Trimmed class itself, should be able to trust that if a Trimmed instance exists, that its contained str will be a trimmed string.
I'm trying to create extension function like this:
fun UHFTAGInfo.toReadUhfTagInfo(): ReadUhfTagInfo {
return ReadUhfTagInfo(this.epc, this.count, this.rssi.toIntOrNull())
}
It is supposed to convert UHFTAGInfo (from java library) to ReadUhfTagInfo (my class in Kotlin).
I'm trying to use it like this:
UHFTAGInfo i = getUHFTAGInfo();
ReadUhfTagInfo ri = i.toReadUhfTagInfo();
At this moment my toReadUhfTagInfo function is at top level, but finally I want to put it in my ReadUhfTagInfo class, like this:
class ReadUhfTagInfo(var epc: String, var cnt: Int, var rssi: Int?)
{
fun UHFTAGInfo.toReadUhfTagInfo(): ReadUhfTagInfo {
return ReadUhfTagInfo(this.epc, this.count, this.rssi.toIntOrNull())
}
}
You can call Kotlin extension functions from Java, sure, but you can't call them with extension function syntax, you must call them like static methods. If you, for example, define
// file: Foo.kt
fun Bar.baz() { ... }
then in Java, you would call this as
FooKt.baz(bar);
I don't think you want to have toReadUhfTagInfo as a member function on the ReadUhfTagInfo class. That would imply that, in order to convert a UHFTAGInfo to a ReadUhfTagInfo, you already need a ReadUhfTagInfo object (which will presumably go unused except to serve as the receiver object.
Extension functions defined inside a class are member extensions and essentially have two receivers.
You can declare extensions for one class inside another class. Inside such an extension, there are multiple implicit receivers - objects whose members can be accessed without a qualifier. An instance of a class in which the extension is declared is called a dispatch receiver, and an instance of the receiver type of the extension method is called an extension receiver.
If you want the extension method to act like a static method in Java (i.e. not require an instance of the enclosing class to execute), then you do the same thing we do with all static methods in Kotlin: We put it in a companion object.
class ReadUhfTagInfo(var epc: String, var cnt: Int, var rssi: Int?)
{
companion object {
fun UHFTAGInfo.toReadUhfTagInfo(): ReadUhfTagInfo {
return ReadUhfTagInfo(this.epc, this.count, this.rssi.toIntOrNull())
}
}
}
As pointed out in the comments, this will still require the name to be imported into the current scope (as all extension methods do), but it won't require a receiver of type ReadUhfTagInfo to call anymore.
When an anonymous object is public, it is simply implemented as an object return, but when it is private, it is returned as a type-cast object. look at the Kotlin code below.
private fun foo() = object {
val x: String = "x"
}
fun bar() = object {
val x: String = "x"
}
When this code is decompiled into Java, it changes like this:
private static final <undefinedtype> foo() {
return (<undefinedtype>)(new Object() {
#NotNull
private final String x = "x";
#NotNull
public final String getX() {
return this.x;
}
});
}
#NotNull
public static final Object bar() {
return new Object() {
#NotNull
private final String x = "x";
#NotNull
public final String getX() {
return this.x;
}
};
}
Therefore, when you try to use the code, only the private anonymous object can access x.
So, why do each access modifiers have different implementations?
That is how language defines the semantics of anonymous objects.
Type of an anonymous object is only usable in the declaring scope, when you use public modifier, anonymous object escapes the current scope and is cast to Any (implicit super type). since Any doesn't have any property x, you can't access it.
on the other hand when you use private modifier you make sure that it doesn't escape the current scope.
From the Kotlin language specification
The main difference between a regular object declaration and an
anonymous object is its type. The type of an anonymous object is a
special kind of type which is usable (and visible) only in the scope
where it is declared. It is similar to a type of a regular object
declaration, but, as it cannot be used outside the declaring scope,
has some interesting effects.
When a value of an anonymous object type escapes current scope:
If the type has only one declared supertype, it is implicitly downcasted to this declared supertype;
If the type has several declared supertypes, there must be an implicit or explicit cast to any suitable type visible outside the
scope, otherwise it is a compile-time error.
Note: in this context “escaping current scope” is performed
immediately if the corresponding value is declared as a non-private
global or classifier-scope property, as those are parts of an
externally accessible interface.
Consider the following class:
class Test {
val classLevel = object {
operator fun invoke() = println("test class level property invocaton")
}
fun foo() {
val functionLevel = object {
operator fun invoke() = println("test invocation")
}
functionLevel() // no problem
classLevel() // Expression 'classLevel' of type 'Any' cannot be invoked as a function. The function 'invoke()' is not found
}
}
Why does the second invoke, the one to the class property, not compile? It is declared the same way as in the function.
I think this is about types.
The classLevel field is of an anonymous type (a subtype of Any, created by the object expression). That type has an invoke() method.
However, that type isn't visible outside the class. So if the property has a getter (i.e. it isn't private), the getter can't return the anonymous type; it has to return the closest named type, which is Any. And Any doesn't have an invoke() method.
I'm not certain whether the code within the class will use the getter method if available, or whether the underlying field's type must exactly match that of the getter if present. But either way, the upshot is clearly that if there's a getter, referring to classLevel within the class gets you an Any reference, and so you can't call invoke() on it. (And you can't down-cast the reference to your object type, which does have invoke(), because that type doesn't have a name.)
One solution, as you found, is to make the field private; that removes the getter, and allows its underlying type to be the actual object type, which is why invoke() is then available to be called.
Another would probably be to define a named type for the object to implement.
According to Kotlin documentation:
Members of the companion object can be called by using simply the
class name as the qualifier.
Why does it not seem to work here?
class Foo {
companion object {
enum class Type { A, B, C }
}
}
class Bar {
val typeA = Foo.Companion.Type.A // works
val typeB = Foo.Type.B // error: "Unresolved reference: Type"
}
Comparing the two qualified type names, Foo.Type.A and Foo.Companion.Type.A , the former would rather mean a type declared directly inside the Foo's scope.
The latter form, therefore, is used to disambiguate types declared inside a type from ones declared inside its nested types and object declarations (including the companion object).
class Foo {
class Bar // Foo.Bar
companion object {
class Bar // Foo.Companion.Bar
}
object Baz {
class Bar // Foo.Baz.Bar
}
}
As Pawel noted, nested types and object declarations are not members and have different resolution rules than those of functions and properties.