How do I define constants in an inner class?

I have an inner class to which I want to add some constants (I’m using Kotlin version 1.3.50). The code looks like following:

public class Adapter {

    public class ViewHolder {

    private inner companion object {
            private const val TYPE_IN_PROGRESS = 1
            private const val TYPE_CANCELLED = 2
            private const val TYPE_FINISHED = 3
    }
}

My IDE (Android Studio) shows a red squiggly under “object” saying:

Companion object is not allowed here

Possible options:

  1. Make ViewHolder a non-inner class - this means it can no longer access methods/properties of Adapter.
  2. Define constants in the Adapter - constants are only used by and relates to ViewHolder so it doesn’t seem right.
  3. Define constants as val (instead of const val) - this will bind it to the class instance (property) and not the class (constants).

My question
Since these aren’t good solutions (except maybe solution 1), is it intended to be like this? If yes then please explain the reasoning behind it so know why it is so. As this can be done in Java so I’m very interested in why it isn’t possible in Kotlin. Thank you. :slight_smile:

This question has also been asked on StackOverflow.

Option 4: emulate an inner class

public class Adapter {

  public class ViewHolder(private val adapter: Adapter) {

    private companion object {
            private const val TYPE_IN_PROGRESS = 1
            private const val TYPE_CANCELLED = 2
            private const val TYPE_FINISHED = 3
    }
  }
}

@fvasco seems like there are many possible workarounds for it. But why is it not possible in Kotlin unlike Java? Would love to know why. :slight_smile:

My gut feeling is that it is not possible in the JVM and Java is just simulating it.

Kotlin generally tries not to simulate features that are not worth enough. In this case I would agree that this feature is not worth simulating.

Edit: sometimes they make the wrong decision though. Like for example when they decided not to simulate package private visibility in Kotlin :wink:

1 Like

Hi @sufianbabri,
unfortunately I don’t know the answer.

However I have to note that a companion of an inner class cannot be a singleton, because I cannot write Adapter.ViewHolder.Companion but only adapter.ViewHolder.Companion, so this Companion may depends by the particular adapter` instance.

As far as I can tell, there is nothing in JVM preventing constants in inner classes. In fact, from what I could tell from looking at the spec, the JVM really does not seem to care much about nested or inner classes and just treats them like any other class.

Java has the following constraint (from the § 8.1.3 in the spec):

It is a compile-time error if an inner class declares a member that is explicitly or implicitly static , unless the member is a constant variable

In Kotlin, the reason why constants cannot be defined in inner classes seems to be a combination of two things:

  1. Constants can only be declared in global scope, object scope, and companion scope.
  2. Inner classes cannot have companions

I understand why the second constraint is there. Since companions are implemented using static members, it would violate the above mentioned constraint in Java, and thus be inaccessible to Java.

The first constraint is the one I am a bit sure about why is there. I see two possible arguments:

  1. Static members are not a thing in Kotlin. Instead, anything that is not associated with a specific instance should be implemented as top level definitions or in companion objects. The restriction on constants may simply be an extension of this philosophy.
  2. It avoids certain ambiguities. For instance, what would this program output:
class Foo {
  const val BAR = 1
  companion object {
    const val BAR = 2
  }
}

fun main() {
  println(Foo.BAR)
}

There you go, it make my assumption stronger, that the JVM does not support static fields in inner classes. The Java language requires them to be constant expressions, because it probably inlines the constant value where it is used.

I do not think that is quite true. I believe that the constraint exists due to a deliberate choice in the design of the Java Programming Language and that it is not strictly required by the JVM. While constants are inlined upon use, they are still defined as static final fields in the compiled class files. This is true even for inner classes. I found absolutely no additional restrictions on inner classes compared to top level classes in the specification of the JVM itself. In fact, as far as I can tell, the only reason that inner classes even exist in the class file format is to make the information about them available to the Java compiler, reflection, debugging, and other tools.

Ok then I need to fallback to @fvasco 's argument. An inner class does not define just one class. For each instance of the outer class, the inner class defines a completely new class. Instances of the lexically same inner class do not have the same type when their outer instance is different.

Be aware that this seems to be true on a Java language level only. In the JVM it can be represented differently to keep it simple.

Acknowledging this, where then should static variables be stored in? It is conceptually not possible to store them. At least not with an intuitive syntax, and definitely not with any existing syntax.

For constant static variables it can be solved in an arguably hacky way in the JVM. Because in the case of constants, it does not influence the Java language level.

I’m confused. Why should an inner class represent arbitrary many types? An inner class is just a class with a reference to the outer class + a special flag that allows it to access private members.
I can’t find any documentation about this claim in either the kotlin nor the java documentation.

Also @fvasco’s claim seems to be wrong. The type of an inner class is Outer.Inner so I don’t see any reason why I shouldn’t be able to type Outer.Inner.Companion (well if companions would be allowed).

class Outer {
    inner class Inner
}
val o = Outer()
val i: Outer.Inner = o.Inner()

In Scala, the enclosing instance is in fact part of the type of an inner class, and you will get a compile-time type error if you try to bind a value of type outer1.Inner to a variable of type outer2.Inner where outer1 and outer2 are different instances of the same class Outer. However, I think Scala is the only language that actually incorporates that view into the type system.

That’s not true. The type of an inner class instance is not Outer.Inner. It is outer.Inner. This is evidenced by the instanceof Operator.

I beg to differ !

In jshell:

jshell> class A { class X {} }

jshell> var a = new A();
a ==> A@5ef04b5

jshell> var b = new A();
b ==> A@5f4da5c3

jshell> var ax = a.new X();
ax ==> A$X@14514713

jshell> var bx = a.new X();
bx ==> A$X@5b37e0d2

jshell> bx = ax; // works just fine, meaning the static type of bx is a supertype of that of ax (and should be true the other way so they are the same type and thus not dependent of enclosing instance)
bx ==> A$X@14514713

jshell> bx instanceof A.X;
$15 ==> true

jshell> bx instanceof a.X; // a.X does not even resolve to a type
|  Error:
|  package a does not exist
|  bx instanceof a.X; // a.X does not even resolve to a type
|                ^-^

(But this would have been true in Scala, indeed)

Ok, I was probably confusing it with what I read about Scala’s typesystem. I am sorry for the confusion I caused.

However, it is still true that this might be a better representation in the type system. Maybe it could be added to Java / Kotlin in the future. Static fields in inner classes would make such future change harder.

I understand the rationale behind inner classes, i.e. the inner class is only used by the outer class, so encapsulate it there.

But is it really worth these contortions to get it to work the way you want?

There are valid use cases for it.

Consider for example a graph that consists of nodes. The Graph is the outer class and the Node is the inner class. You want a graph instance to accept nodes of its own inner Node type only.

In my opinion leveraging this potential feature would be the only valid use case for (non-static) inner classes. Normally I discourage using (non-static) inner classes

A key use case is anonymous inner classes that are used as event handlers. Of course we now have lambdas for that. In Java Kotlin. Where I actually use them is for “views” such as iterator implementations.

Encapsulation is the main reason for nested classes of all kinds. In Kotlin even more than in Java, since private members of member classes really are private with respect to the enclosing class.

Now, as to proper “inner” classes, yes I wonder. After all, it’s just some syntactic sugar in order to avoid declaring a reference to an enclosing instance, isn’t it? So why all the fuss?

Inner classes à la Scala make more sense, because they additionally give some more type safety (I am thinking about @fatjoe79’s graph and nodes), but this also comes with added complexity in the type system (is it still worth it?).

1 Like