Kotlin: cannot access enum defined in companion object - kotlin

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.

Related

Why it is forbidden to use 'out' keyword in generics if a method excepts the type parameter as a parameter?

I'm looking for an example that can cause a problem when using out in class declaration and the class has a method that get the parameter type as argument.
Also I'm looking for an example that can cause a problem when using in in class declaration and the parameter type is a var member of the class?
I think that i will be able to understand the rules only by examples
Suppose these are the classes we are working with:
open class Animal
class Cat: Animal() {
fun meow() = println("meow")
}
If we create a class like this with covariant out type and the compiler allowed us to use the type as a function parameter:
class Foo<out T: Animal> {
private var animal: T? = null
fun consumeValue(x: T) { // NOT ALLOWED
animal = x
}
fun produceValue(): T? {
return animal
}
}
Then if you do this, it will be lead to an impossible situation where we are trying to call meow on an Animal that doesn't have a meow function:
val catConsumer = Foo<Cat>()
val animalConsumer: Foo<Animal> = catConsumer // upcasting is valid for covariant type
animalConsumer.consumeValue(Animal())
catConsumer.produceValue()?.meow() // can't call `meow` on plain Animal
And if we create a class like this with contravariant in type and the compiler allowed us to use the type as a return value:
class Bar<in T: Animal>(private val library: List<T>) {
fun produceValue(): T { // NOT ALLOWED
return library.random()
}
}
Then if you do this, it will lead to the compiler impossibly casting a return type to a subtype.
val animalProducer: Bar<Animal> = Bar(List(5) { Animal() })
val catProducer: Bar<Cat> = animalProducer // downcasting is valid for contravariant type
catProducer.produceValue().meow() // can't call `meow` on plain Animal
A property has a getter which is just like a function that returns a value, and a var property additionally has a setter, which is just like a function that takes a parameter. So val properties are not compatible with contravariance (in) and var properties are not compatible with contravariance or covariance (out). Private properties aren't encumbered by these restrictions because within the class's inner workings, the type is invariant. All the class can know about its own type is its bounds. Variance just affects how the class can be cast (viewed) by the outside world.
So an example with val is enough to show why any property is incompatible with contravariance. You could replace val with var below and it would be no different.
class Bar<in T: Animal>(
val animal: T // NOT ALLOWED
)
val animalProducer: Bar<Animal> = Bar(Animal())
val catProducer: Bar<Cat> = animalProducer // downcasting is valid for contravariant type
catProducer.animal.meow() // can't call `meow` on plain Animal
Small reminder about variance
When you have a generic class G<T> (parameterized type), the variance is about defining a relationship between the hierarchy of the types G<T> for different Ts, and the hierarchy of the different Ts themselves.
For instance, if child class C extends a parent P then:
does List<C> extend List<P>? (List<T> would be covariant in T)
or the reverse? (contravariant)
or is there no relationship between List<C> and List<P>? (invariant).
Example
Now, consider List<out T>, which means that List is covariant in T.
As we've just seen, declaring list as such means that the following holds: "if C extends P, then List<C> extends List<P>".
Let's assume the following class declarations here:
open class Parent {
fun doParentStuff()
}
class Child : Parent() {
fun doChildStuff()
}
The covariance of List<out T> means that this is possible:
val listOfChild: List<Child> = listOf<Child>(Child(), Child())
// this is ok because List is covariant in T (out T)
// so List<Child> is a subtype of List<Parent>, and can be assigned to listOfParent
val listOfParent: List<Parent> = listOfChild
So what would happen if we could declare a method in the List class that accepts a parameter T?
class List<out T> {
fun add(element: T) {
// I can guarantee here that I have an instance of T, right?
}
}
The rules of most languages (including Kotlin) state that if a method accepts a parameter of type T, you can technically get an instance of T or any subclass of T (this is the point of subclassing), but you have at least all the API of T available to you.
But remember that we declared List<out T>, which means I can do:
val listOfChild: List<Child> = listOf<Child>(Child(), Child())
// this is ok because List is covariant in T (out T)
val listOfParent: List<Parent> = listOfChild
// listOfChild and listOfParent point to the same list instance
// so here we are effectively adding a Parent instance to the listOfChild
listOfParent.add(Parent())
// oops, the last one is not an instance of Child, bad things will happen here
// we could fail right here at runtime because Parent cannot be cast to Child
val child: Child = listOfChild.last
// even worse, look at what looks possible, but is not:
child.doChildThing()
Here you can see that from within the List<Child> instance, we actually could receive an instance of Parent which is not a subclass of Child in a method that had declared a parameter of type Child.

Check instance of anonymous object

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

Java allow to access Kotlin's base variable through it's child, but not Kotlin, why?

I have a class as below
open class KotlinBase {
companion object {
const val TAG = "testing"
}
}
And a child of it as
class KotlinChild : KotlinBase()
When I try to access TAG from a Java class, I could either
public class JavaOther {
String test1 = KotlinBase.TAG; // This is okay
String test2 = KotlinChild.TAG; // This is okay
}
However, when accessing from Kotlin class, I can't access through the Child.
class KotlinOther {
val test1 = KotlinChild.TAG // Compile/Syntax error
val test2 = KotlinBase.TAG // This is okay
}
Why can't my Kotlin class access the inherited variable TAG through KotlinChild?
It's a design decision allowing you to avoid ambiguities. - child classes can have their own companion objects with fields/methods having same names as those in the parent.
By restricting access to companions only through the actual class, problems with ambiguous field/method shadowing do not exist anymore.
Also, companion objects are not static members known from other languages. Although, the majority of use cases overlap.
Additionally, remember that
KotlinBase.TAG
is a shortcut for:
KotlinBase.Companion.TAG

Why I don't see companion object component of extended class?

I have an abstract class:
abstract class Vec2t
and an extending class:
class Vec2 : Vec2t
Vec2t has the following companion object:
companion object {
#JvmField val length = 2
}
But when I type Vec2.length, then it's marked as unresolved reference...
Why? What am I missing?
In Kotlin, a companion object is just a specially marked object inside your class. You can omit its name, and it will get the default name of Companion, and you also get the convenience of being able to use the MyClass.myProperty syntax instead of MyClass.Companion.myProperty to access its members. It is, however, still just a nested object.
Imagine how things would work if it was a regular nested object instead of a companion:
abstract class Vec2t {
object LengthKeeper {
val length = 2
}
}
class Vec2 : Vec2t()
You could access the length via Vec2t.LengthKeeper.length, but of course you couldn't access it as Vec2.LengthKeeper.length, because the Vec2 class does not have a nested object called LengthKeeper.
Marking a variable inside the companion object #JvmStatic does generate a static variable for length inside Vec2t in the bytecode, but you can only access that from Java, where writing the following does in fact work with your code:
Vec2 v = new Vec2();
int length = Vec2.getLength();
As for solving this in Kotlin, if you really have to access the property of the base class through Vec2 with that syntax, you'll probably have to do something like this:
class Vec2 : Vec2t() {
companion object {
val length get() = Vec2t.length
}
}

Why can't I access private class methods in the class's companion object in Scala?

I'm working on a homework assignment for my object oriented design class, and I'm running into trouble with Scala's companion objects. I've read in a few places that companion objects are supposed to have access to their companion class's private methods, but I can't seem to get it to work. (Just as a note, the meat of the assignment had to do with implementing a binary search tree, so I'm not just asking for answers...)
I have an object that is supposed to create an instance of my private class, BstAtlas (Bst is also defined in the Atlas object, took it out for clarity):
object Atlas {
def focusRoom(newRoom:Room,a:Atlas):Atlas = a.helpFocusRoom(newRoom);
abstract class Atlas {
...
protected def helpFocusRoom(n:Room):Atlas;
...
}
private class BstAtlas(bst:Bst) extends Atlas {
...
protected def helpFocusRoom(newRoom:Room):Atlas = ...
// uses some of bst's methods
...
}
}
But when I go to compile, I get the following error:
Question23.scala:15: error: method
helpFocusRoom cannot be accessed in
Atlas.Atlas
a.helpFocusRoom(newRoom);
The function helpFocusRoom needs to be hidden, but I don't know how to hide it and still have access to it inside of the companion object.
Can anyone tell me what I'm doing wrong here?
The problem is that classes and companion objects can't be nested like that. To define a companion object, you need to define the class outside of the object's body but in the same file.
Companion objects should be next to their real object, not containing it:
object Example {
class C(val i: Int = C.DefaultI) { }
object C { protected val DefaultI = 5 }
}
scala> (new Example.C).i
res0: Int = 5
scala> Example.C.DefaultI
<console>:11: error: value DefaultI cannot be accessed in object Example.C
Example.C.DefaultI
Alternatively, you can alter the scope of the protected keyword to include the enclosing object:
object Example {
def value = (new D).hidden
class D(val i: Int = 5) {
protected[Example] def hidden = i*i
}
}
scala> Example.value
res1: Int = 25
but here you ought not name the outer object the same thing as the inner class or you'll have trouble referring to it from within the class.