我在Oracle的网站上读到了Java的类型擦除.
什么时候发生类型擦除?在编译时还是运行时?当班级加载?当类被实例化时?
很多站点(包括上面提到的官方教程)都说在编译时会发生类型擦除.如果在编译时完全删除了类型信息,那么当调用使用泛型的方法而没有类型信息或错误的类型信息时,JDK如何检查类型兼容性?
考虑以下示例:Say class A
有一个方法,empty(Box<? extends Number> b)
.我们编译A.java
并获取类文件A.class
.
public class A {
public static void empty(Box<? extends Number> b) {}
}
Run Code Online (Sandbox Code Playgroud)
public class Box<T> {}
Run Code Online (Sandbox Code Playgroud)
现在我们创建另一个类B
,该类empty
使用非参数化参数(原始类型)调用该方法:empty(new Box())
.如果我们编译B.java
与A.class
在类路径中,javac的是足够聪明,引发警告.所以A.class
有一些类型信息存储在其中.
public class B {
public static void invoke() {
// java: unchecked method invocation:
// method empty in class A is applied to given types
// required: Box<? extends java.lang.Number>
// found: …
Run Code Online (Sandbox Code Playgroud) 我知道内联函数可能会提高性能并导致生成的代码增长,但我不确定何时正确使用它.
lock(l) { foo() }
Run Code Online (Sandbox Code Playgroud)
编译器可以发出以下代码,而不是为参数创建函数对象并生成调用.(来源)
l.lock()
try {
foo()
}
finally {
l.unlock()
}
Run Code Online (Sandbox Code Playgroud)
但我发现kotlin没有为非内联函数创建的函数对象.为什么?
/**non-inline function**/
fun lock(lock: Lock, block: () -> Unit) {
lock.lock();
try {
block();
} finally {
lock.unlock();
}
}
Run Code Online (Sandbox Code Playgroud) 在 Kotlin 中,鉴于“reified”关键字只能用于内联函数的泛型类型参数,为什么还要有 reified 关键字呢?为什么 Kotlin 编译器(至少在未来)不能自动将内联函数的所有泛型类型参数视为具体化?
我发现人们在看到这个“具体化”一词时感到恐慌,并要求我不要使代码变得复杂。因此就有了这个问题。
在 Kotlin 中,我正在尝试编译以下内容:
以下代码无法编译,因为在以下位置需要类型:
fun getMapper(animalType: AnimalType): Printer
我尝试使用<Any>
or<*>
但没有成功。有人可以帮忙吗?
(通过将下面的代码复制粘贴到https://try.kotlinlang.org很容易看到错误)
enum class AnimalType {
CAT, DOG
}
class Dog
class Cat
interface Printer<in T> {
fun mapToString(input: T): String
}
class DogPrinter : Printer<Dog> {
override fun mapToString(input: Dog): String {
return "dog"
}
}
class CatPrinter : Printer<Cat> {
override fun mapToString(input: Cat): String {
return "cat"
}
}
private fun getMapper(animalType: AnimalType): Printer {
return when(animalType) …
Run Code Online (Sandbox Code Playgroud) 只能将具体化的类型参数与内联函数一起使用。所以如果我想要一个类的这样的参数,我需要一个这样的技巧:
class Foo<T : Any>(private val clazz: KClass<T>) {
companion object {
inline fun <reified T: Any> create() = Foo(T::class)
}
}
Run Code Online (Sandbox Code Playgroud)
然后我可以创建这样的实例Foo
:
val foo = Foo.create<Bar>()
Run Code Online (Sandbox Code Playgroud)
在Foo
我可以访问clazz
但我的问题是clazz
当我需要调用需要具体化类型参数的方法时我可以使用吗?
例如,Foo
我想添加一个这样的方法:
fun print(list: List<Alpha>) {
list.filterIsInstance<T>().forEach { print(it) }
}
Run Code Online (Sandbox Code Playgroud)
但据我所知,没有办法从 clazz
这里可以用作类型参数的东西。
是的,我知道有一种形式filterIsInstance
需要 aClass
所以我可以这样做:
list.filterIsInstance(clazz.java).forEach { print(it) }
Run Code Online (Sandbox Code Playgroud)
然而,许多库都包含两种形式(显式类参数和具体化类型参数)的方法 不提供。
例如 Jackson Kotlin Extensions.kt。实际上这不是一个很好的例子,因为非具体化的等价物都是单行的,但情况并非总是如此——然后你最终将具体化类型参数方法的实现解包到你的代码中。
我正在创建http json客户端。我将Volley与协程结合使用。我想创建通用的HTTP客户端,以便可以在任何地方使用它。
我创建了通用扩展方法来将JSON字符串解析为对象。
inline fun <reified T>String.jsonToObject(exclusionStrategy: ExclusionStrategy? = null) : T {
val builder = GsonBuilder()
if(exclusionStrategy != null){
builder.setExclusionStrategies(exclusionStrategy)
}
return builder.create().fromJson(this, object: TypeToken<T>() {}.type)
Run Code Online (Sandbox Code Playgroud)
}
问题是,当我调用此方法时,无法得到预期的结果。第一次通话会给出正确的结果。对象已初始化。但是第二次调用(我使用传递给方法的通用参数)以异常“ LinkedTreeMap无法转换为令牌”结束。
protected inline fun <reified T>sendRequestAsync(endpoint: String, data: Any?, method: Int, token: Token?): Deferred<T> {
return ioScope.async {
suspendCoroutine<T> { continuation ->
val jsonObjectRequest = HttpClient.createJsonObjectRequest(
endpoint,
data?.toJsonString(),
method,
Response.Listener {
//this call is successful and object is initialized
val parsedObject : HttpResponse<Token> = it.toString().jsonToObject()
//this call is not successful and …
Run Code Online (Sandbox Code Playgroud) 我正在尝试使用具有reified类型的函数作为扩展函数,但我不认为这是可能的,因为在我检查生成的字节码后我发现方法签名是私有的,是否有任何解决方法使其公开?
CommonExtensions.kt
inline fun<reified T: Activity> Context.startActivity() {
val intent = Intent(this, T:: class.java)
startActivity(intent)
}
fun View.visible() {
visibility = View.VISIBLE
}
Run Code Online (Sandbox Code Playgroud)
Kotlin字节码:
private final static startActivity(Landroid/content/Context;)V
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
...
Run Code Online (Sandbox Code Playgroud)
客户代码:
Kotlin文件
override fun showMessageEmptyOfferFeeds() {
mOfferFeedsWarning.visible() // "visible()" extension func RESOLVED
}
Run Code Online (Sandbox Code Playgroud)
Java文件
showProfileDetailsUi(){
startActivity<DetailActivity>() //"startActivity()" extension func NOT RESOLVED
}
Run Code Online (Sandbox Code Playgroud) java kotlin kotlin-android-extensions kotlin-reified-type-parameters
我正在调查创建代理对象的库是如何工作的,特别是我想了解它们如何从声明的方法中获取类型.例如Android的流行库 - Retrofit:
interface MyService {
@GET("path")
Call<MyData> getData();
}
Run Code Online (Sandbox Code Playgroud)
我很困惑 - 如何才能从这个界面获得正确的MyData类而不是原始对象?我的理解类型擦除的原因将删除放置在通用大括号内的任何信息.
我写了一些测试代码,令我惊讶的是从这样的代码获取类型真的很容易:
@org.junit.Test
public void run() {
Method[] methods = Test.class.getDeclaredMethods();
Method testMethod = methods[0];
System.out.println(testMethod.getReturnType());
ParameterizedType genericType = ((ParameterizedType) testMethod.getGenericReturnType());
Class<Integer> clazz = (Class<Integer>) genericType.getActualTypeArguments()[0];
System.out.println(clazz);
}
interface Test {
List<Integer> test();
}
Run Code Online (Sandbox Code Playgroud)
它看起来有点脏,但它的工作和打印Integer
.这意味着我们在运行时有类型.此外,我已经阅读了有关匿名类的另一个脏技巧:
System.out.println(new ArrayList<Integer>().getClass().getGenericSuperclass());
Run Code Online (Sandbox Code Playgroud)
AbstractList<E>
这段代码打印原始
System.out.println(new ArrayList<Integer>() { }.getClass().getGenericSuperclass());
Run Code Online (Sandbox Code Playgroud)
打印ArrayList<Integer>
.
这并不是让我困惑的最后一件事.在Kotlin中有一些具体的泛型,在编译时看起来像是一些黑客,但我们可以轻松地从泛型中获取类:
inline fun <reified T> test() {
print(T::class)
}
Run Code Online (Sandbox Code Playgroud)
而现在我完全混淆了类型擦除机制.
new ArrayList<Integer>
呢?更新: 4.如何在Kotlin中使用具体化的泛型以及为什么这样的酷事不能用Java实现?
这里解释了如何使用仿制药. …
我可以使用MutableList<MDetail>
Gson正确地使用代码1保存到json字符串,但是当我尝试MutableList<MDetail>
使用代码2从json字符串恢复对象时出现错误。我搜索了一些资源,看来我需要注册InstanceCreator。
如何InstanceCreator
用Kotlin 写一个注册码?谢谢!
错误
Caused by: java.lang.RuntimeException: Unable to invoke no-args constructor for interface model.DeviceDef. Registering an InstanceCreator with Gson for this type may fix this problem.
Run Code Online (Sandbox Code Playgroud)
代码1
private var listofMDetail: MutableList<MDetail>?=null
mJson = Gson().toJson(listofMDetail) //Save
Run Code Online (Sandbox Code Playgroud)
代码2
var mJson: String by PreferenceTool(this, getString(R.string.SavedJsonName) , "")
var aMListDetail= Gson().fromJson<MutableList<MDetail>>(mJson)
inline fun <reified T> Gson.fromJson(json: String) = this.fromJson<T>(json, object: TypeToken<T>() {}.type)
Run Code Online (Sandbox Code Playgroud)
我的课
interface DeviceDef
data class BluetoothDef(val status:Boolean=false): DeviceDef
data class WiFiDef(val name:String, val status:Boolean=false) : …
Run Code Online (Sandbox Code Playgroud) 我正在开始一个新项目,我将与10个开发人员组成一个团队。我正在为我们的Android应用设置基础结构。在与团队合作时,我希望每个人都遵循相同的结构,即为ViewModel
每个人创建fragment
并使用数据绑定。我如何使其严格,以使开发人员在未ViewModel
为其片段创建代码时出错?
所以我创建了以下内容BaseFragment
:
abstract class BaseFragment<out VM : BaseViewModel, DB : ViewDataBinding> : Fragment() {
open lateinit var binding: DB
private fun init(inflater: LayoutInflater, container: ViewGroup?) {
binding = DataBindingUtil.inflate(inflater, getLayoutRes(), container, false)
}
@LayoutRes
abstract fun getLayoutRes(): Int
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
init(inflater, container)
super.onCreateView(inflater, container, savedInstanceState)
return binding.root
}
open fun refresh() {}
}
Run Code Online (Sandbox Code Playgroud)
我该如何进一步改善?
android android-fragments kotlin android-architecture-components
我正在使用 Kotlin 来解析 JSON。例如,我有一个国家的表示:{"code":"US", "name":"United States of America"}
。Country
为了从这样的 a生成一个对象JSONObject
,我有这个函数:
val produceCountry = fun (js: JSONObject) =
Country(js.getString("code"), js.getString("name"))
Run Code Online (Sandbox Code Playgroud)
我可以使用这个函数轻松解析数组Country
。然而,除了 数组之外Country
,我还有Cat
、Car
、Cart
、CordlessPhone
等数组。每个数组都有自己的produce*
函数,将 a 转换JSONObject
为该类型的 Kotlin 对象。为了概括数组解析,我有这个函数:
fun <T> produceSetOf(array: JSONArray, element: (JSONObject) -> T): Set<T> {
val set = mutableSetOf<T>()
for (i in 0 until array.length())
set.add(element(array.getJSONObject(i)))
return set
}
Run Code Online (Sandbox Code Playgroud)
所以我可以调用produceSetOf(jsonArray, produceCountry)
遇到一个元素类型为 的数组Country
。这也适用于Cat
, Car …
Kotlin 官方文档和这个答案很好地解释了 Kotlin 如何reified
让我们改变以下内容:
myJsonString.toData(MyDataClass::class)
Run Code Online (Sandbox Code Playgroud)
到:
myJsonString.toData<MyDataClass>()
Run Code Online (Sandbox Code Playgroud)
但我认为两者都不能很好地解释动机。reified 函数只是因为它节省了几个字符而更可取吗?或者不必将类作为参数传递还有其他好处吗?
kotlin ×11
generics ×5
java ×5
kotlin-reified-type-parameters ×3
json ×2
android ×1
android-architecture-components ×1
arrays ×1
coroutine ×1
function ×1
gson ×1
jvm ×1
type-erasure ×1