Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin cannot access protected abstract method

Tags:

kotlin

I have the following classes structure:

abstract class Abstr{
    protected abstract fun m()
}

class Child : Abstr(){
    private val subChild: Abstr = Child()

    override fun m() = subChild.m()// Error:(12, 18) Kotlin: Cannot access 'm': it is protected in 'Abstr'
}

I got an exception Kotlin: Cannot access 'm': it is protected in 'Abstr'

It little bit confusing, because the same structure is legal for java.

According to kotlin docs

  • protected — visible inside this class only + visible in subclasses too;

Is it bug or expected behavior?

like image 524
SergiiGnatiuk Avatar asked Oct 30 '17 12:10

SergiiGnatiuk


People also ask

Can we override protected method in Kotlin?

In Kotlin if you override a protected member and do not specify the visibility explicitly, the overriding member will also have protected visibility. In Java the visibility is according to the modifier and the default is still public .

Can we declare abstract method as protected?

Yes, you can declare an abstract method protected. If you do so you can access it from the classes in the same package or from its subclasses.

Can we use protected in abstract class?

Abstract classes are similar to interfaces. You cannot instantiate them, and they may contain a mix of methods declared with or without an implementation. However, with abstract classes, you can declare fields that are not static and final, and define public, protected, and private concrete methods.

Can we inherit abstract class Kotlin?

A Kotlin abstract class is similar to Java abstract class which can not be instantiated. This means we cannot create objects of an abstract class. However, we can inherit subclasses from a Kotlin abstract class.


2 Answers

It is designed behavior

Protected modifier in Kotlin similar to Java, but has additional restrictions.

Protected in Java:

  • Visible for inheritance
  • Visible in package

Protected in Kotlin:

  • Visible for inheritance

So, according to the code in question we cannot access protected method

class Child : Abstr(){
    private val subChild: Abstr = Child()

    override fun m() = subChild.m() //Trying to access not inherited method 
}

There is similar restriction in Java, when we trying to access protected member from another package:

// FILE: a/SuperClass.java
package a;
public class SuperClass {
    protected void superFunction() {}
}

// FILE: b/ChildClass.java
package b;
public class ChildClass extends SuperClass {
    void testFunction() {
        ((SuperClass) new ChildClass()).superFunction(); // ERROR: superFunction() has protected access in a.SuperClass
    }
}

There is answer in issue tracker from Kotlin team: https://youtrack.jetbrains.com/issue/KT-21048

like image 82
SergiiGnatiuk Avatar answered Oct 15 '22 13:10

SergiiGnatiuk


The current behavior is by design.

By calling subChild.m() you're trying to access an instance of Abstr from outside the object, so protected access prevents you from doing this.

Let me show you a short example to clarify the case

    abstract class ParentCl {
        protected var num = 1
        protected open fun m(){
        }
    }
    
    class ChildCl : ParentCl() {
        private val a0 : ParentCl = ChildCl()
        override fun m() {
            super.m() // 1-st case
            num = 2 // 2-nd case            
            a0.m() // 3-rd case
        }
    }
  1. You're calling the protected ParentCl's fun from the child class. It will work fine.
  2. You're modifying of protected variable from the child class. It will work fine.
  3. You're calling the protected fun outside the context of the child class. This will not work.

Depends on what was your goal there are two solutions:

  1. If you wanted to call m() from ParentCl you need to change the visibility from protected to internal or public.
  2. If you wanted to call m() from the child class you need to declare the variable without the explicit type of its parent: private val subChild = Child().

Note: in case you will use m() from other children of ParentCl you need to enlarge the visibility scope inside child class: public override fun m() {...}

like image 28
Val Avatar answered Oct 15 '22 12:10

Val