Add smart cast to val properties

public val properties for interface does’t do smart casting to the object that were set into them.

There is an option do declare 2 variables. one public interface and one private instance. but I prefer just one

what I wish to have:

class MyClass{
    val myObservable: Observable<Unit> = PublishSubject.create<Unit>()

    fun foo(){
        myObservable.onNext(Unit) // smart casting doesn't work, but should
    }
}

what works but involves boilerplate code

class MyClass{
    private val myPublisher = PublishSubject.create<Unit>()
    val myObservable: Observable<Unit> = myPublisher

    fun foo(){
        myPublisher.onNext(Unit)
    }
}
3 Likes

Have you tried to remove explicit type declaration? I do not understand, what do you mean by smart cast in this case.

1 Like

I what the class to reveal whoever uses it Observable but inside the class use PublishSubject
The same approach can also happen with LiveData where inside the class (ViewModel) you want MutableLiveData but reveal the Activity/Fragment just LiveData

1 Like

Sorry, but I still do not understand, what you mean. There is a standard pattern in Kotlin where you reveal mutable something inside the class and read-only something outside. Like:

private val _list: MutableList<Int>
val list: List<Int> get() = list

It add another line to the code, but there are not ways around it, because the types are indeed different inside the class and outside.

It force me to double the amount of variables in my class. If smart casting can solve it (as casting should) why shouldn’t we have it? It’s a val param anyways.

There are workaround. Notice I mentioned your solution in my original post. It doesn’t mean we need to do it every single time.
In my class’s functions I’ll have to type the undrscore param and not the regular. I dont like this boilerplate and I believe it can easily be fixed

1 Like

Simply, what he wants is that to expose the val property as an interface or superclass type (a “general” type if you wish), but then use it in the same code in his class as a property of the actual, declared type.
For example, let’s suppose that he has a val like this:

class MyClass{
    val myCharSequence: CharSequence = "this is a string" 

    fun foo() {
        //Note that plus is a function that is defined on the String class, and not the CharSequence interface
        println(myCharSequence.plus(" that got plused by another string") //Wouldn't run because it wouldn't be smart casted
    }
}

So, in other words, he wants to hide the implementation details of the val so that any outside class can safely use it regardless of what the library decides to use as the actual underlying type while still being able to call any functions that is defined on the actual underlying type without needing to cast because the compiler should be a 100% sure that the var’s type is of that underlying type.

As a temporary solution for now, I think that OP could just cast the val at the start of every function to the type that he wants and then call .also{ preferredNameForCastedVal -> //Code }

@darksnake

I really don’t get what am I missing, why should we do it if it doubles our code?
we know that this will work

 val myObservable: Observable<Unit> = PublishSubject.create<Unit>()

 fun foo() {
        if (myObservable is PublishSubject<Unit>) {   // always true because it's a val 
            myObservable.onNext(Unit)
        }
    }

don’t you feel it’s more elegant without the redundant “if”
or without having 2 parameters like we both suggested?

First of all, you are doing it wrong:

private val _observable = PublishSubject.create()
val observable: Observable get() = _observable

Doing this, you can call both internal and external value without effort. It costs you a single line, not doubling the code.

Second, I see a complain, but I do not see a solution. How can you tell which type to use for internal use and which for external use? Won’t it cost the same additional line if you had a syntax to do it? There are several tickets about it in the tracker, but nobody managed to propose more concise syntax than it is now.

1 Like
  • you are right, doubles the parameters, not the code. still, I have many of these :frowning:
  • I do suggest a solution. smart casting can tell the value real type by scanning the code. if that’s the case, and val property cannot be changed, kotlin should allow us to know the real type inside the class.

Interesting, I’d like to see such feature in kotlin. really smart cast

How if I could write coding following:

fun createShape() : Shape {
  val shape : Shape
  if(...) {
    shape = Circle()
    shape.radius = 1 // smart cast
  } else {
    ...
  }
  shape.color = Red
  return shape
}

A bad example. First - it won’t compile because shape is read only. Second, shape = Circle().also{it.radius = 1} will do it for you.

1 Like

FYI - there is already a feature request for that on Kotlin tracking system
https://youtrack.jetbrains.com/issue/KT-18532
(https://youtrack.jetbrains.com/issue/KT-32014)

It does compile. And more, to my surprise, the smart cast also works. :upside_down_face:

https://pl.kotl.in/aqr-9JftY

1 Like

You are right. It is a special case of late assign. If you try to reassign it or make not so obvious assign logic there will be exception.