Max Schippers
Max Schippers

Reputation: 53

Kotlin: Expression can't be invoked because function invoke() is not found

I'm trying to build an app where I implement Google maps. for some reason I get the error that the expression can't be invoked because the function invoke() is not found. I don't know how to fix this maybe one of you guys can help?

    package com.example.maxs.kotlinnearbyv2

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.example.maxs.kotlinnearbyv2.Common.Common
import com.example.maxs.kotlinnearbyv2.Model.MyPlaces
import com.example.maxs.kotlinnearbyv2.Remote.IGoogleAPIService
import com.google.android.gms.maps.*
import com.google.android.gms.maps.model.BitmapDescriptorFactory

import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import kotlinx.android.synthetic.main.activity_maps.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class MapsActivity : AppCompatActivity(), OnMapReadyCallback {

private lateinit var mMap: GoogleMap

private var latitude:Double=0.toDouble()
private var longitude:Double=0.toDouble()

lateinit var mService:IGoogleAPIService

internal var currentPlace: MyPlaces?=null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_maps)
    // Obtain the SupportMapFragment and get notified when the map is ready to be used.
    val mapFragment = supportFragmentManager
        .findFragmentById(R.id.map) as SupportMapFragment
    mapFragment.getMapAsync(this)

    //Init Service
    mService = Common.googleApiService

    bottom_navigation_view.setOnNavigationItemReselectedListener {item ->
        when(item.itemId)
        {
            R.id.action_hospital -> nearByPlace("hospital")
            R.id.action_restaurant -> nearByPlace("restaurant")
            R.id.action_market -> nearByPlace("market")
            R.id.action_school -> nearByPlace("school")
        }
    }
}

private fun nearByPlace(typePlace: String) {

    //Clear all marker on Map
    mMap.clear()
    //build URL request base on location
    val url = getUrl(latitude,longitude, typePlace)

    mService.getNearByPlaces(url)
        .enqueue(object : Callback<MyPlaces>{
            override fun onResponse(call: Call<MyPlaces>, response: Response<MyPlaces>) {

                currentPlace = response.body()

                if(response!!.isSuccessful)
                {
                    for(i in 0 until response!!.body()!!.results!!.size)
                    {
                        val markerOptions=MarkerOptions()
                        val googlePlace = response.body().results!!(i)
                        val lat = googlePlace.geometry!!.location!!.lat
                        val lng = googlePlace.geometry!!.location!!.lng
                        val placeName = googlePlace.name
                        val latLng = LatLng(lat, lng)

                        markerOptions.position(latLng)
                        markerOptions.title(placeName)
                        if (typePlace.equals("hospital"))
                            markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_local_hospital_black_24dp))
                        else if (typePlace.equals("market"))
                            markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_shopping_cart_black_24dp))
                        else if (typePlace.equals("restaurant"))
                            markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_restaurant_black_24dp))
                        else if (typePlace.equals("school"))
                            markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_school_black_24dp))
                        else
                            markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))

                        markerOptions.snippet(i.toString())

                        //add marker to map
                        mMap!!.addMarker(markerOptions)


                    }
                    //move camera
                    mMap!!.moveCamera(CameraUpdateFactory.newLatLng(LatLng(latitude, longitude)))
                    mMap!!.animateCamera(CameraUpdateFactory.zoomTo(15.0f))
                }
            }

            override fun onFailure(call: Call<MyPlaces>, t: Throwable) {
                Toast.makeText(baseContext, ""+t!!.message,Toast.LENGTH_SHORT).show()
            }

        })
}

private fun getUrl(latitude: Double, longitude: Double, typePlace: String): String {

    val googlePlaceUrl = StringBuilder("https://maps.googleapis.com/maps/api/place/nearbysearch/json")
    googlePlaceUrl.append("?location=$latitude,$longitude")
    googlePlaceUrl.append("&radius=10000") //10 km
    googlePlaceUrl.append("&type=$typePlace")
    googlePlaceUrl.append("&key=")

    Log.d("URL_DEBUG", googlePlaceUrl.toString())
    return googlePlaceUrl.toString()
}

/**
 * Manipulates the map once available.
 * This callback is triggered when the map is ready to be used.
 * This is where we can add markers or lines, add listeners or move the camera. In this case,
 * we just add a marker near Sydney, Australia.
 * If Google Play services is not installed on the device, the user will be prompted to install
 * it inside the SupportMapFragment. This method will only be triggered once the user has
 * installed Google Play services and returned to the app.
 */
override fun onMapReady(googleMap: GoogleMap) {
    mMap = googleMap

    // Add a marker in Sydney and move the camera
    val barbier = LatLng(52.391274, 6.449712)
    mMap.addMarker(MarkerOptions().position(barbier).title("Marker in Barbier"))
    mMap.moveCamera(CameraUpdateFactory.newLatLng(barbier))
    mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(barbier, 15.0f))
}
}

I can't seem to find any solution and i'm probably think way to difficult… the error is giving at the response.body()!!.results!!(i)

val googlePlace = response.body().results!!(i)

it sure is driving me crazy right now.

Upvotes: 5

Views: 22202

Answers (2)

Zoe - Save the data dump
Zoe - Save the data dump

Reputation: 28298

As Roland mentioned, () is the invoke operator, and [] is the index operator. () is used for, among others, functions:

fun demo(func: (i: Int) -> Unit){
    // These are identical
    func(1)
    func.invoke(1)
}

[] is the index operator, which is what you want to apply here.

It can be used for any class that has an operator fun get(args)

class Demo {
    // These don't actually need a return type either. Or any arguments at all. 
    // If this was an int-containing class (i.e. a List), this would be the conventional declaration
    operator fun get(index: Int) : Int {
        return 0 // Obviously with an actual return value. 
    }

    // But they don't have to. They can have multiple, or no arguments. 
    operator fun get(index: Int, fallback: Int) : Int{
        return fallback
    }
}

fun test(){
    val demo = Demo()
    //Depending on arguments, the use is different. Single-arg is basic, and traditional:
    val value = demo[12];
    // But the multi-arg one can be useful depending on the class.
    val value2 = demo[12, 3];
}

I'm aware you didn't ask about declaring these, but the code is a part of my point:

  • The index operator is applied to any class with an operator fun get, with any number of input args
  • Lists, maps, and arrays have this method.

So you want to use [index], not (index). Alternatively, you could use the method, and use .get(index) directly. If you want to use null-safe calls (?.), you have to use .get(index).

Also, you should generally prefer null-safe calls, optionally combined with ?.let{ }, ?.forEach { }, or similar, over using null assertion. First of all, it kinda defeats one of the core parts of Kotlin: Null safety. Secondly, if it ever is null, the app will crash instead of gracefully telling the user "Something went wrong". I am not familiar with the library you're using, so I'm honestly not sure what's null when, and if it can be null even if it is successful.

As for let and forEach, they're easier to use when you have nullability. Consider this:

someNullableIterable?.forEach {
    // Only invoked if the iterable isn't null
}

Compared to:

if(someNullableIterable!= null){
    for(item in someNullableIterable!!) { // Needs !! if the variable is global, and it's a var and not a val. 
        // Foo bar
    }
}

There's a bunch of similar functions too, and if you need to use indices as well, there's forEachIndexed. But just using forEach (alternatively forEachIndexed) will shorten some of your code, and better allow you to deal with nullability.

Upvotes: 2

Roland
Roland

Reputation: 23352

To access an element in an array or list, use square brackets, e.g.:

array[i]
list[i] // or list.get(i)
results!![i]

Regarding the error message: Kotlin was assuming an invoke operator, which you didn't supply. You may want to read what an invoke-operator is good for. Sometimes it comes in very handy. But for your problem the square brackets should suffice.

As a (further) side-note: don't write your code with tons of !!, rather try to identify what can be null in the first place and omit the rest if it doesn't suit your needs, e.g.:

response?.also {
  if (it.isSuccessful) {
    it.body()?.results?.forEach {
      //...
    }
  }
}

Just a start... you may then want to simplify things even more... Just omit !! whenever possible... You may also want to read about null safety in Kotlin and maybe also about the smart casts.

Your typePlace.equals(...-conditions can also perfectly be replaced with when, e.g.:

when(typePlace) {
  "hospital" -> ...
  "market" -> ...

That combined with either let or also may even further reduce your code, but that will probably be another story, better suited for code review.

Upvotes: 4

Related Questions