I started learning Kotlin recently and I'd like to implement a simple postfix notation parser were instances of MathOperator interface pick values from the stack and return the result.
The implementation is trivial in plain Java
public interface MathOperator {
public float evaluate(Deque<Float> stack);
public MathOperator ADD = stack -> stack.pop() + stack.pop();
public MathOperator SUB = stack -> -stack.pop() + stack.pop();
public MathOperator MUL = stack -> stack.pop() * stack.pop();
}
However, when I try the same in Kotlin I get "Property getter or setter expected" error.
interface KTMathOperator {
fun evaluate(stack: Deque<Float>): Float
companion object {
val ADD: KTMathOperator = (stack) -> stack.pop() + stack.pop()
}
}
How can I implement the same interface and its properties in Kotlin concisely?
Unfortunately, in Kotlin SAM conversions are not so handy. You can rewrite your example like:
interface KTMathOperator {
fun evaluate(stack: Deque<Float>): Float
companion object {
val ADD: KTMathOperator = object : KTMathOperator {
override fun evaluate(stack: Deque<Float>): Float = stack.pop() + stack.pop()
}
}
}
also you can change evaluate to invoke. Then you can your ADD just like ADD(stack)
interface KTMathOperator {
operator fun invoke(stack: Deque<Float>): Float
companion object {
val ADD: KTMathOperator = object : KTMathOperator {
override fun invoke(stack: Deque<Float>): Float = stack.pop() + stack.pop()
}
}
}
fun main() {
val list = LinkedList<Float>().apply {
add(1.0f)
add(1.0f)
}
println(KTMathOperator.ADD(list)) // will print 2.0
}
If you're not too worried about maximum performance and a little strange code, you can also write like this :)
class KTMathOperator(private val operation: (Deque<Float>) -> Float) : (Deque<Float>) -> Float {
override fun invoke(stack: Deque<Float>): Float = operation(stack)
companion object {
val ADD: KTMathOperator = KTMathOperator { stack -> stack.pop() + stack.pop() }
}
}
Related
I'm learning Android programming with Kotlin. I do have a basic understanding of OO programming and lambdas.
The general syntax to assign a lambda to a variable, as I understand it, is as follows:
val somevar [: ( parm1-type [, parm2-type [,...] ] -> return-type ] = { [ parm1 [, parm2 [,...] ] -> ] stmt1 [; stmt2 [;...] ] }
The current example in the book has a variable called resultObserver which is initialized with a lambda:
val resultObserver = Observer<Float> { result -> resultText.text = result.toString() }
What is the Observer<Float> in that statement doing? It doesn't seem to fit the syntax above. What am I missing?
Here is some more of the coding sample in case it matters to answer my question:
class MainFragment : Fragment() {
...
private lateinit var viewModel: MainViewModel
override fun onCreateView( ...
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
val resultObserver = Observer<Float> { result -> resultText.text = result.toString() }
viewModel.getResult().observe( viewLifecycleOwner, resultObserver )
...
}
}
class MainViewModel : ViewModel() {
...
private var result: MutableLiveData<Float> = MutableLiveData()
...
fun getResult() : MutableLiveData<Float> {
return result
}
}
You are creating an anonymous instance of Observer interface
//Observer from documentation
public interface Observer<T> {
/**
* Called when the data is changed.
* #param t The new data
*/
void onChanged(T t);
}
//**** traditional way ****
class FloatObserver: Observer<Float>{
override fun onChanged(t: Float?) {
}
}
val resultObserver = FloatObserver()
//**** lambda way ****
//doing it in lambda way, as interface has single method, you can use it as lambda
val resultObserver = Observer<Float> { t -> }
Hi I am new to programming and trying to implement MVP pattern by passing generic Presenter class LoginPresenter to Generic Model Class LoginUserModel but getting type mismatch error.
on loginUserModel.onAttach(this)
and I am unable to figure out how to pass pass generic interface to another class.
Login Presenter
class LoginPresenter<V : ILoginView>: BasePresenter<V>(), ILoginPresenter<V> {
lateinit var loginUserModel: LoginUserModel<ILoginPresenter<ILoginView>>
lateinit var iLoginPresenter: ILoginPresenter<V>
.........
.........
override fun setupModel() {
iLoginPresenter = this
loginUserModel = LoginUserModel()
// here i am getting error
/**
Type mismatch.
Required:
ILoginPresenter<ILoginView>
Found:
LoginPresenter<V>
*/
loginUserModel.onAttach(this)
}
}
Login Model
class LoginUserModel<P: ILoginPresenter<ILoginView>> : LoginModelContract<P> {
var iLoginPresenter : P? = null
override fun onAttach(ILoginPresenter: P) {
iLoginPresenter = ILoginPresenter
}
}
LoginModelContract
public interface LoginModelContract<P: ILoginPresenter<ILoginView>> {
fun getUsersList(
userName:String,
guid: String
)
fun onAttach(ILoginPresenter: P)
fun onDetatch()
fun getPresenter(): P?
}
You can use two generic statements like below
class LoginUserModel<V: ILoginView, P : ILoginPresenter<V>> : LoginModelContract<V,P> {
var iLoginPresenter : P? = null
override fun onAttach(ILoginPresenter: P) {
iLoginPresenter = ILoginPresenter
}
}
interface ILoginView{
}
interface ILoginPresenter<T>{
fun setupModel()
}
class LoginPresenter<V : ILoginView>: ILoginPresenter<V> {
lateinit var loginUserModel: LoginUserModel<V,ILoginPresenter<V>>
lateinit var iLoginPresenter: ILoginPresenter<V>
override fun setupModel() {
iLoginPresenter = this
loginUserModel = LoginUserModel()
loginUserModel.onAttach(this)
}
}
public interface LoginModelContract<V: ILoginView, P : ILoginPresenter<V>> {
fun onAttach(ILoginPresenter: P)
}
In java, i hava code like below:
public class GenericTest {
private static final GenericTest instance = new GenericTest();
IRequirement requirement;
public static GenericTest getInstance() {
return instance;
}
public class ClassA {}
public interface InterfaceA {}
public void init(IRequirement requirement){
this.requirement = requirement;
}
public interface IRequirement {
<T extends ClassA & InterfaceA> T supply();
}
class ClassB {
void doSomeThing() {
ClassA a = requirement.supply();
InterfaceA ia = requirement.supply();
}
}
}
I can get ClassA or InterfaceA instance according to my needs.
But in kotlin, same codes like below:
open class ClassA(val name: String) {
fun function1() {}
fun function2() {}
}
interface InterfaceA {
fun iFunction1()
}
class ModuleX private constructor() {
var requirement: IRequirement? = null
companion object {
val instance = ModuleX()
}
fun init(requirement: IRequirement) {
instance.requirement = requirement
}
interface IRequirement {
fun <T> supply(): T where T : ClassA, T : InterfaceA
}
}
object ClassB {
inline fun <reified T> doSomeThing() where T : ClassA, T : InterfaceA{
val require = ModuleX.instance.requirement?.supply<T>()
require?.function1()
require?.iFunction1()
}
fun doAnotherThing() {
// IDE give an error when calling supply()
val require = ModuleX.instance.requirement?.supply()
require?.function1()
require?.iFunction1()
}
}
I must specify the actual type of the generic, or use the generic method, otherwise I will not be able to use the IRequirement in the code above, such as in ClassB.doAnotherThing() where IDE give an error "Type inference failed: Not enough information to infer parameter T in fun supply( ): T where T : InterfaceAPlease specify it explicitly."
My question is: In my module, it is required to provide a class that extends ClassA and implements InterfaceAd, but the module does not know the exact type of the class because it is outside the module. How should I use generics in this case?
fun doAnotherThing() {
val require: Any? = ModuleX.instance.requirement?.supply()
if (require != null) {
(require as ClassA).function1()
(require as InterfaceA).iFunction1()
}
}
class ModelFactory {
fun setA() : ModelFactory {
// blabla...
}
fun setB() : ModelFactory {
// blabla...
}
fun setC() : ModelFactory {
// blabla...
}
fun build() : Model {
// An error occurs if any of setA, setB, and setC is not called.
}
}
//example
fun successTest() {
ModelFactory().setA().setB().setC().build() // No error occurs at compile time
}
fun failTest() {
ModelFactory().setA().build() // An error occurs at compile time because setB and setC are not called.
}
It's awkward grammatically, but I think it's been expressed what I want.
I have already implemented an error-raising runtime for this requirement, but I want to check this at compile time.
If possible, I think I should use annotations. But is this really possible at compile time?
With Kotlin, I have been avoiding builder pattern, as we can always specify default values for non-mandatory fields.
If you still want to use a builder pattern, you can use Step builder pattern that expects all mandatory fields to be set before creating the object. Note that each setter method returns the reference of next setter interface. You can have multiple Step builders based on the combination of mandatory fields.
class Model(val a: String = "", val b: String = "", val c: String = "")
class StepBuilder {
companion object {
fun builder(): AStep = Steps()
}
interface AStep {
fun setA(a: String): BStep
}
interface BStep {
fun setB(b: String): CStep
}
interface CStep {
fun setC(c: String): BuildStep
}
interface BuildStep {
//fun setOptionalField(x: String): BuildStep
fun build(): Model
}
class Steps : AStep, BStep, CStep, BuildStep {
private lateinit var a: String
private lateinit var b: String
private lateinit var c: String
override fun setA(a: String): BStep {
this.a = a
return this
}
override fun setB(b: String): CStep {
this.b = b
return this
}
override fun setC(c: String): BuildStep {
this.c = c
return this
}
override fun build() = Model(a, b , c)
}
}
fun main() {
// cannot build until you call all three setters
val model = StepBuilder.builder().setA("A").setB("B").setC("C").build()
}
I followed the tutorial: https://kotlinlang.org/docs/tutorials/native/mpp-ios-android.html and successfully export a jar file for Android and a framework for iOS. After I want to implement something more complex. I uses Android Studio Kotlin with the codes below:
Model.kt:
package org.kotlin.mpp.mobile.BusinessLogic
abstract class Model{
var _id:Long = 0
abstract fun PolymorphismTest()
}
Sales.kt:
package org.kotlin.mpp.mobile.BusinessLogic
class Sales : Model() {
init {
this._id = _counter
_counter++
}
companion object {
private var _counter: Long = 0
}
fun get_counter(): Long {
return _counter
}
private val _salesItems:MutableList<SalesItem> = ArrayList()
fun SalesItems(): MutableList<SalesItem> {
return _salesItems
}
fun TotalAmount():Double
{
var totalAmount:Double = 0.0
for(aSalesItem in _salesItems)
{
totalAmount += aSalesItem.SubTotal()
}
return totalAmount
}
fun AddSalesItem(salesItem: SalesItem)
{
this._salesItems.add(salesItem)
}
fun AddSalesItem(itemName:String, itemCode:String, quantity:Double, amount:Double )
{
val aSalesItem = SalesItem()
aSalesItem._itemCode = itemCode
aSalesItem._itemName = itemName
aSalesItem._quantity = quantity
aSalesItem._amount = amount
this.AddSalesItem(aSalesItem)
}
fun ToString(): String {
return "Sales: $this._id"
}
override fun PolymorphismTest() {
println("This is method from Sales")
}
}
SalesItem.kt:
package org.kotlin.mpp.mobile.BusinessLogic
class SalesItem : Model() {
init {
this._id = _counter
_counter++
}
companion object {
private var _counter: Long = 0
}
fun get_counter(): Long {
return _counter
}
var _sales: Sales? = null
var _amount:Double = 0.toDouble()
var _quantity:Double = 0.toDouble()
fun SubTotal(): Double {
return _amount * _quantity
}
var _itemName:String? = null
var _itemCode:String? = null
fun Sales():Sales?{
return _sales
}
fun SalesItem(sales:Sales)
{
_sales = sales
this._id = _counter
_counter++
}
fun ToString(): String {
return "Sales: $this._id"
}
override fun PolymorphismTest() {
println("This is method from SalesItem")
}
}
I export these codes into a framework then imported into Xcode and using Swift to call
ViewController.swift
import UIKit
import SharedCode
class ViewController: UIViewController{
override func viewDidLoad(){
super.viewDidLoad()
print("Creating Sales Object")
let sales = Sales() //error here
}
}
After that I met the errors of
Instances of kotlin.Error, kotlin.RuntimeException and subclasses aren't propagated from Kotlin to Objective-C/Swift.
Other exceptions can be propagated as NSError if method has or inherits #Throws annotation.
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen org.kotlin.mpp.mobile.BusinessLogic.Sales.Companion#228b588
at 0 SharedCode
Kotlin/Native has different threading model. The idea is that an instance of an object has to be frozen to be accessed from all threads. There is .freeze() extension method for that.
By default, object Smth are also frozen. In the code sniper you have mutable fields in companion object.
A possible workaround could be to replace companion object with an ordinary class, that you create explicitly
https://kotlinlang.org/docs/reference/native/concurrency.html#concurrency-in-kotlinnative
https://kotlinlang.org/docs/reference/native/immutability.html#immutability-in-kotlinnative