My question is almost like this question Java: calling outer class method in anonymous inner class .
But this time we are in Kotlin.
As the example below, I want to call funB() in the object expression, but I only made two failures.
class A {
lateinit var funA: () -> Unit
lateinit var funB: () -> Unit
fun funC() {
var b = object : B() {
override fun funB() {
funA() // A.funA()
// Two attempts to fail
funB() // b.funB(), not my expect
A::funB() // compile error
}
}
}
}
Thank you for your answer!
You can qualify this with a # to obtain an equivalent of java: MyClass.this
->this#MyClass
Then in your case, you can call:
this#A.funB()
From the doc:
To access this from an outer scope (a class, or extension function, or labeled function literal with receiver) we write this#label where #label is a label on the scope this is meant to be from:
class A { // implicit label #A
inner class B { // implicit label #B
fun Int.foo() { // implicit label #foo
val a = this#A // A's this
val b = this#B // B's this
val c = this // foo()'s receiver, an Int
val c1 = this#foo // foo()'s receiver, an Int
val funLit = lambda# fun String.() {
val d = this // funLit's receiver
}
val funLit2 = { s: String ->
// foo()'s receiver, since enclosing lambda expression
// doesn't have any receiver
val d1 = this
}
}
}
}
Related
I have a function in Kotlin which takes a particular string as input. Depending on the input, I want to create a variable of a specific type and do some computations on it.
For example,
fun compute(input: String): Any{
if(input=="2d"){
var point: Point2D;// Points2D - x: int, y: int
//initilize and do some computations
return point.findDistanceFromOrigin()
}else if(input=="2d-1"){
var point: Point2DWithP1AsOrigin;// Point2DWithP1AsOrigin - x: int, y: int
//initilize and do some computations
return point.findDistanceFromOrigin()
}else if(input=="2d-2"){
var point: Point2DWithP2AsOrigin;
//initilize and do some computations
return point.findDistanceFromOrigin()
}
.
.
.
}
You can see in the above example, I want to initilize the type of point depending on the input and do computation and return.
All the if-else conditions have the same code except for the definition of the variable. How can I put all this in a single block with something like this:
var point: if(input=="2d) Point2D::class else if(input=="2d-1") Point2DWithP1AsOrigin::class.....
How can I do that?
You could do something like this
fun compute(input: String): Any{
val point: MyPoint = when(input) {
"2d" -> Point2D()
"2d-1" -> Point2DWithP1AsOrigin()
"2d-2" -> Point2DWithP2AsOrigin()
else -> Point2D() //fallback is necessary
}
//initilize and do some computations
return point.findDistanceFromOrigin()
}
But then it's essential that all those classes share the same interface. Because they need to have the same methods in order to do the same operations on them.
For example like this:
class Point2D : MyPoint {
override fun findDistanceFromOrigin() = 5
}
class Point2DWithP1AsOrigin : MyPoint{
override fun findDistanceFromOrigin() = 6
}
class Point2DWithP2AsOrigin : MyPoint{
override fun findDistanceFromOrigin() = 7
}
interface MyPoint {
fun findDistanceFromOrigin() : Int
}
You can store constructor references and then invoke required one
fun main() {
val constructors = mapOf(
"2d" to ::Point2D,
"2d-1" to ::Point2DWithP1AsOrigin,
"2d-2" to ::Point2DWithP2AsOrigin,
)
val type = "2d-2"
val constructor = constructors[type] ?: throw IllegalArgumentException("$type not supported")
val point = constructor()
println(point::class)
}
Output
class Point2DWithP2AsOrigin
I've created a Kotlin equivalent of TypeReference<T> like so:
abstract class TypeReference<T> : Comparable<T> {
val type: Type get() = getGenericType()
val arguments: List<Type> get() = getTypeArguments()
final override fun compareTo(other: T): Int {
return 0
}
private fun getGenericType(): Type {
val superClass = javaClass.genericSuperclass
check(superClass !is Class<*>) {
"TypeReference constructed without actual type information."
}
return (superClass as ParameterizedType).actualTypeArguments[0]
}
private fun getTypeArguments(): List<Type> {
val type = getGenericType()
return if (type is ParameterizedType) {
type.actualTypeArguments.toList()
} else emptyList()
}
}
In order to obtain Class<*> of the generic type and its arguments, I've also created the following extension function (and this is where I believe the problem lies, since this is where the stack trace fails).
fun Type.toClass(): Class<*> = when (this) {
is ParameterizedType -> rawType.toClass()
is Class<*> -> this
else -> Class.forName(typeName)
}
I'm unit testing this like so:
#Test
fun `TypeReference should correctly identify the List of BigDecimal type`() {
// Arrange
val expected = List::class.java
val expectedParameter1 = BigDecimal::class.java
val typeReference = object : TypeReference<List<BigDecimal>>() {}
// Act
val actual = typeReference.type.toClass()
val actualParameter1 = typeReference.arguments[0].toClass()
// Assert
assertEquals(expected, actual)
assertEquals(expectedParameter1, actualParameter1)
}
The problem I think, lies in the extension function else -> Class.forName(typeName) as it throws:
java.lang.ClassNotFoundException: ? extends java.math.BigDecimal
Is there a better way to obtain the Class<*> of a Type, even when they're generic type parameters?
You need to add is WildcardType -> ... branch to your when-expression to handle types like ? extends java.math.BigDecimal (Kotlin equivalent is out java.math.BigDecimal), ?(Kotlin equivalent is *), ? super Integer(Kotlin equivalent is in java.math.Integer):
fun Type.toClass(): Class<*> = when (this) {
is ParameterizedType -> rawType.toClass()
is Class<*> -> this
is WildcardType -> upperBounds.singleOrNull()?.toClass() ?: Any::class.java
else -> Class.forName(typeName)
}
Note that in this implementation single upper bound types will be resolved as its upper bound, but all other wildcard types (including multiple upper bounds types) will be resolved as Class<Object>
https://github.com/pluses/ktypes
val typeReference = object : TypeReference<List<BigDecimal>>() {}
val superType = typeReference::class.createType().findSuperType(TypeReference::class)!!
println(superType.arguments.first())// List<java.math.BigDecimal>
println(superType.arguments.first().type?.arguments?.first())// java.math.BigDecimal
Problem Statement: I'm trying to recreate Scala/Finagle's andThen method chaining/composition across two types: Filters and Services.
The goal is to be able to do something like this:
val f1 = Filter1()
val f2 = Filter2()
val s3 = Service3()
val pipeline = f1 andThen f2 andThen s3
val result = pipeline(4) //execute pipeline with integer value of 4
Filters should be combinable with other filters and also a service to "end a chain". Services should also be combinable with other services. Both seem to lead to Unresolved reference andThen
Existing non-working solution:
typealias Transformer<A,B> = (A) -> B
abstract class Service<A,B>: Transformer<A,B> {
//DOESN'T WORK
infix fun <A,B,C> Service<A,B>.andThen(f: Service<B,C>): Service<A,C> {
val left = this
return object : Service<A, C>() {
override fun invoke(p1: A): C {
return f(left.invoke(p1))
}
}
}
}
typealias TwoWayTransformer<A,B,C,D> = (A, Service<C,D>) -> B
abstract class Filter<A,B,C,D>: TwoWayTransformer<A,B,C,D> {
//DOESN'T WORK
infix fun <A,B,E,F> Filter<A,B,C,D>.andThen(next: Filter<C,D,E,F>): Filter<A,B,E,F> {
val left = this
return object: Filter<A,B,E,F>() {
override fun invoke(a: A, service: Service<E,F>): B {
val s = object: Service<C,D>() {
override fun invoke(c: C): D { return next.invoke(c,service) }
}
return left.invoke(a,s)
}
}
}
//DOESN'T WORK
infix fun <A,B,C,D> Filter<A,B,C,D>.andThen(next: Service<C,D>): Service<A,B> {
val left = this
return object: Service<A,B>() {
override fun invoke(a: A): B {
return left.invoke(a, next)
}
}
}
}
Sidebar:
Filter<A,B,C,D> can stitch with Filter<C,D,E,F> which can stitch with Service<E,F> - the last two types of the left must match with the first two of the right when doing left andThen right.
A Filter<A,B,C,D> is simply a function of type: (A, Service<C,D>) -> E which simplifies further to (A, C->D) -> E
Link to working fiddle with example services/filters: https://pl.kotl.in/yIx80SzDF
The signatures you need are
infix fun <C> andThen(f: Service<B,C>): Service<A,C>
infix fun <E,F> andThen(next: Filter<C,D,E,F>): Filter<A,B,E,F>
infix fun andThen(next: Service<C,D>): Service<A,B>
Never add any type variables to functions that are already declared in the definition of the class. Never add an extra receiver for the class itself.
The following code is posted on Kotlin's website:
class A { // implicit label #A
inner class B { // implicit label #B
fun Int.foo() { // implicit label #foo
val a = this#A // A's this
val b = this#B // B's this
val c = this // foo()'s receiver, an Int
val c1 = this#foo // foo()'s receiver, an Int
val funLit = lambda# fun String.() {
val d = this // funLit's receiver
}
val funLit2 = { s: String ->
// foo()'s receiver, since enclosing lambda expression
// doesn't have any receiver
val d1 = this
}
}
}
}
It isn't clear to me how you call a function in an inner class. For example, how do you call Int.foo()
var a = A()
a.Int.foo() // This is not allowed.
Lets look at a more simple example:
class A {
inner class B {
fun foo() {
// ...
}
}
}
To call a function within an inner class, you must access it using an instance of the outer class, like so:
A().B().foo()
What makes your example more difficult is that Int.foo() is an extension function, so to access it you must call foo() on an Int within the same scope as the extension function:
class A { // outer class A
inner class B { // inner class B
fun Int.foo() { // entension function foo
print("Foo called on integer $this")
}
fun caller(i: Int) { // calls extension function
i.foo()
}
}
}
fun main() {
A().B().caller(10) // calls extension function in inner class B
}
Here we have added a function caller which is in the same scope as the extension function. The code outputs the following:
Foo called on integer 10
In this case, foo is an extension function defined in B. You cannot call these member extension functions from outside by default. However, it's possible to execute the function when you get into the scope of B, which can be achieved with scoping functions such as with. Please be aware that this extension function can only be called on instances of Int.
val a = A()
val b = a.B()
with(b) {
5.foo()
}
Currently I have a private function which returns a Pair<User, User> object. The first user is the sender of something, the second user is the receiver of that thing.
I think this Pair<User, User> is not enough self explanatory - or clean if you like - even though it's just a private function.
Is it possible to return with an ad-hoc object like this:
private fun findUsers(instanceWrapper: ExceptionInstanceWrapper): Any {
return object {
val sender = userCrud.findOne(instanceWrapper.fromWho)
val receiver = userCrud.findOne(instanceWrapper.toWho)
}
}
and use the returned value like this:
// ...
val users = findUsers(instanceWrapper)
users.sender // ...
users.receiver // ...
// ...
?
If not, what's the point of ad-hoc object in Kotlin?
Since the type can not be denoted in the language, use return type inference:
class Example {
private fun findUsers(instanceWrapper: ExceptionInstanceWrapper) =
object {
val sender = userCrud.findOne(instanceWrapper.fromWho)
val receiver = userCrud.findOne(instanceWrapper.toWho)
}
fun foo() = findUsers(ExceptionInstanceWrapper()).sender
}
Another option would be to devise a data class:
class Example {
private data class Users(val sender: User, val receiver: User)
private fun findUsers(instanceWrapper: ExceptionInstanceWrapper): Users {
return Users(
sender = userCrud.findOne(instanceWrapper.fromWho),
receiver = userCrud.findOne(instanceWrapper.toWho)
)
}
fun foo() = findUsers(ExceptionInstanceWrapper()).sender
}
Simply define your function as a lambda.
Here's simple object I've just written as an example in another context:
private val Map = {
val data = IntArray(400)
for (index in data.indices) {
data[index] = index * 3
}
object {
val get = { x: Int, y: Int ->
data[y * 20 + x]
}
}
}
fun main() {
val map = Map()
println(map.get(12,1))
}
Unfortunately, you cannot assign a type name, so it can be used as a return value but not as an argument. Maybe they'll make this possible so we can finally do OOP JS style.
Alternatively, they could implement object types equivalent to function types but that could end up being too wordy. You could then do a typedef but that would actually just be a kind of class definition 😅
Another option is to have a generic class for return types:
data class OutVal<T>(private var v: T?) {
fun set(newVal: T) {
v = newVal
}
fun get() = v
}
Usage example:
private fun findUsers(instanceWrapper: ExceptionInstanceWrapper,
sender: OutVal<String>, receiver: OutVal<String>) {
sender.set(userCrud.findOne(instanceWrapper.fromWho))
receiver.set(userCrud.findOne(instanceWrapper.toWho))
}
val sender = OutVal("")
val receiver = OutVal("")
findUsers(instanceWrapper, sender, receiver)
sender.get() // ...
receiver.get() // ...