【问题标题】:Kotlin: Expression can't be invoked because function invoke() is not foundKotlin:无法调用表达式,因为找不到函数 invoke()
【发布时间】:2018-11-01 05:54:23
【问题描述】:

我正在尝试构建一个应用来实现 Google 地图。由于某种原因,我收到无法调用表达式的错误,因为找不到函数 invoke() 。我不知道如何解决这个问题,也许你们中的一个可以帮忙?

    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))
}
}

我似乎找不到任何解决方案,而且我可能觉得很难…… 错误是在 response.body()!!.results!!(i)

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

这确实让我快疯了。

【问题讨论】:

    标签: kotlin


    【解决方案1】:

    要访问数组或列表中的元素,请使用方括号,例如:

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

    关于错误消息:Kotlin 假设您没有提供 invoke operator。您可能想了解调用运算符的用途。有时它会派上用场。但是对于您的问题,方括号就足够了。

    作为(进一步的)旁注:不要用大量的!! 编写代码,而是首先尝试确定可以是null 的内容,如果不适合您,则省略其余部分需求,例如:

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

    只是一个开始...然后您可能想进一步简化事情...尽可能省略!!...您可能还想了解null safety in Kotlin,也可能还想了解smart casts

    您的typePlace.equals(...-条件也可以完美地替换为when,例如:

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

    结合letalso 可能会进一步减少您的代码,但这可能是另一回事,更适合code review

    【讨论】:

      【解决方案2】:

      正如 Roland 所说,() 是调用运算符,[] 是索引运算符。 () 用于,除其他外,功能:

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

      []index operator,这是您要在此处应用的。

      它可以用于任何具有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];
      }
      

      我知道你没有问过声明这些,但代码是我的观点的一部分:

      • 索引运算符适用于具有operator fun get 的任何类,具有任意数量的输入参数
      • 列表、映射和数组都有这种方法。

      所以你想使用[index],而不是(index)。或者,您可以使用该方法,并直接使用.get(index)。如果你想使用 null-safe 调用 (?.),你必须使用 .get(index)

      此外,您通常应该更喜欢 null-safe 调用,可以选择与 ?.let{ }?.forEach { } 或类似的组合,而不是使用 null 断言。首先,它有点打败了 Kotlin 的核心部分之一:Null 安全性。其次,如果它为空,应用程序将崩溃,而不是优雅地告诉用户“出了点问题”。我不熟悉您正在使用的库,所以老实说,我不确定什么时候为 null,以及即使成功也可以为 null。

      至于letforEach,当您具有可空性时,它们更易于使用。考虑一下:

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

      相比:

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

      还有很多类似的功能,如果你也需要使用索引,还有forEachIndexed。但是仅仅使用forEach(或者forEachIndexed)会缩短你的一些代码,并更好地让你处理可空性。

      【讨论】:

        猜你喜欢
        • 2017-02-06
        • 2022-01-25
        • 2022-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-18
        • 1970-01-01
        相关资源
        最近更新 更多