Kotlin基础(十一):反射和注解

前言

本文主要讲解kotlin反射和注解。


Kotlin文章列表

Kotlin文章列表: 点击此处跳转查看


目录

在这里插入图片描述


1.1 kotlin反射

1.1.1 kotlin反射概念和常见使用场景

在Kotlin中,反射是一种能够在运行时动态地获取、检查和操作类、属性、方法等结构的能力。Kotlin为反射提供了一组API,这些API允许你在运行时获取类的信息并与其交互,而不需要在编译时知道类的确切结构。虽然反射功能非常强大,但它也可能导致性能下降和类型安全性降低,因此应该谨慎使用。下面是反射的常见使用场景:

  1. 动态加载类和创建对象:通过反射,你可以在运行时根据类名动态地加载类,并且使用反射创建对象实例。
  2. 获取和设置属性值:使用反射,你可以获取和设置对象的属性值,即使属性是私有的。
  3. 调用方法:反射允许你在运行时调用类的方法,包括私有方法。
  4. 分析注解:通过反射,你可以检查类、属性、方法等是否带有特定的注解,并相应地执行某些逻辑。
  5. 获取类的信息:你可以通过反射获取类的构造函数、方法、属性、父类等信息,这对于编写通用代码或工具类非常有用。
  6. 动态代理:使用反射,你可以在运行时创建接口的代理实现,从而实现动态代理。
  7. 插件系统:反射可以帮助你实现灵活的插件系统,使应用程序能够在运行时加载和使用插件。

需要注意的是,反射可能会导致运行时的性能开销,因为在编译时无法进行类型检查,而且代码更加脆弱,容易出错。因此,除非必要,最好避免过度使用反射。如果有其他更好的替代方案,应该优先考虑使用那些方案。


1.1.2 kotlin反射常见用法

Kotlin反射(Reflection)是指在运行时检查、访问和修改程序的结构、属性和方法,它为程序员提供了一种动态处理程序元素的方式。下面是Kotlin中常见的反射用法:

  1. 获取Kotlin类的KClass对象:
    使用::class语法可以获取一个Kotlin类的KClass对象。例如:val kClass = MyClass::class
  2. 获取Kotlin类的实例对象:
    使用KClass对象的createInstance()方法可以在不知道具体类名的情况下创建类的实例。例如:val instance = kClass.createInstance()
  3. 获取Kotlin类的属性:
    使用KClass对象的memberProperties属性可以获取类的所有属性,然后可以进一步获取属性的名称、类型、可见性等信息。
  4. 获取Kotlin类的函数:
    使用KClass对象的memberFunctions属性可以获取类的所有函数,然后可以进一步获取函数的名称、参数、返回类型等信息。
  5. 调用Kotlin类的函数:
    使用KClass对象的call()方法可以调用类的无参函数。如果是有参数的函数,可以通过callBy()方法传递参数。
  6. 获取Kotlin类的构造函数:
    使用KClass对象的constructors属性可以获取类的所有构造函数,然后可以进一步获取构造函数的参数信息。
  7. 修改Kotlin类的属性:
    使用KClass对象的memberProperties属性获取类的属性,然后使用setValue()方法可以修改属性的值。
  8. 获取Kotlin对象的KFunction对象:
    使用Kotlin对象的::functionName语法可以获取对象的KFunction对象,然后可以使用反射调用该函数。

注意:在使用反射时,需要注意性能和安全性。由于反射是在运行时进行的,所以可能会引入性能损耗,并且由于编译器无法进行类型检查,可能会导致类型错误或安全问题。因此,在使用反射时,应该尽量避免频繁使用,除非没有其他替代方案。

以上就是kotlin常见用法,具体内容下面会讲解。


1.1.3 kotlin获取Class对象

在Kotlin中,获取一个类的Class对象有多种方法:

  1. 使用::class语法:
    最简单的方法是在类名后面加上::class,这将返回该类的KClass对象,然后可以通过java属性来获取Class对象。例如:

    val classObj: Class<MyClass> = MyClass::class.java
    
  2. 使用Class.forName()
    如果你知道类的全限定名(包名+类名),可以使用Class.forName()方法来获取Class对象。例如:

    val className = "com.example.MyClass"
    val classObj: Class<*> = Class.forName(className)
    
  3. 使用对象的javaClass属性:
    对于已经存在的对象,可以通过访问对象的javaClass属性来获取其Class对象。例如:

    val myObject = MyClass()
    val classObj: Class<out MyClass> = myObject.javaClass
    

以上就是kotlin获取一个类的Class对象方法。


1.1.4 kotlin获取类的构造函数Constructor

在 Kotlin 中,可以使用反射来获取类的构造函数 Constructor。构造函数可以通过类的 KClass 对象来访问。以下是获取类的构造函数的示例代码:

import kotlin.reflect.KClassdata class Person(val name: String, val age: Int)fun main() {// 获取类的 KClass 对象val personClass: KClass<Person> = Person::class// 获取类的所有构造函数val constructors = personClass.constructors// 打印每个构造函数的参数列表for (constructor in constructors) {println("Constructor: $constructor")constructor.parameters.forEach {println("Parameter: ${it.name} - ${it.type}")}}
}

在上面的示例中,我们定义了一个 Person 类,并使用 Person::class 来获取其 KClass 对象。然后,我们通过 constructors 属性获取类的所有构造函数。对于每个构造函数,我们通过 parameters 属性获取构造函数的参数列表,并打印出每个参数的名称和类型。

请注意,通过反射获取的属性在 Kotlin 中表示为 KProperty 对象,而在 Java 中表示为 java.lang.reflect.Field 对象。


1.1.5 kotlin获取类的成员变量

在 Kotlin 中,可以使用反射来获取类的成员变量(属性)。成员变量可以通过类的 KClass 对象来访问。以下是获取类的成员变量的示例代码:

import kotlin.reflect.KVisibility
import kotlin.reflect.full.memberPropertiesdata class Person(val name: String, val age: Int)fun main() {// 获取类的 KClass 对象val personClass = Person::class// 获取类的所有成员变量val memberProperties = personClass.memberProperties// 打印每个成员变量的名称、类型和可见性for (property in memberProperties) {println("Property: ${property.name} - ${property.returnType}")println("Visibility: ${property.visibility}")}
}

在上面的示例中,我们定义了一个 Person 类,并使用 Person::class 来获取其 KClass 对象。然后,我们通过 memberProperties 属性获取类的所有成员变量(属性)。对于每个属性,我们打印出其名称、类型和可见性。

请注意,通过反射获取的属性在 Kotlin 中表示为 KProperty 对象,而在 Java 中表示为 java.lang.reflect.Field 对象。


1.1.6 kotlin获取类的成员函数

在 Kotlin 中,可以使用反射来获取类的成员函数(方法)。成员函数可以通过类的 KClass 对象来访问。以下是获取类的成员函数的示例代码:

import kotlin.reflect.full.memberFunctionsclass MyClass {fun sayHello() {println("Hello!")}fun addNumbers(a: Int, b: Int): Int {return a + b}
}fun main() {// 获取类的 KClass 对象val myClass = MyClass::class// 获取类的所有成员函数val memberFunctions = myClass.memberFunctions// 打印每个成员函数的名称、参数和返回类型for (function in memberFunctions) {println("Function: ${function.name}")println("Parameters:")function.parameters.forEach { parameter ->println("${parameter.name}: ${parameter.type}")}println("Return type: ${function.returnType}")println("-------------------")}
}

在上面的示例中,我们定义了一个名为 MyClass 的类,其中包含两个成员函数 sayHelloaddNumbers。然后,我们使用 MyClass::class 获取 MyClass 类的 KClass 对象。接着,我们通过 memberFunctions 属性获取类的所有成员函数。最后,我们遍历每个成员函数,并打印出它们的名称、参数以及返回类型。

请注意,通过反射获取的成员函数在 Kotlin 中表示为 KFunction 对象,而在 Java 中表示为 java.lang.reflect.Method 对象。


1.1.7 kotlin获取类的相关信息

在 Kotlin 中,可以使用反射(Reflection)来获取类的相关信息。通过反射,您可以获得类的名称、属性、函数、构造函数等信息。以下是一些常见的获取类相关信息的方法:

  1. 获取类的名称:
    使用 ::class 语法可以获取类的 KClass 对象,然后可以通过 simpleName 属性获取类的名称。例如:

    val className = MyClass::class.simpleName
    
  2. 获取类的包名:
    使用 ::class 语法可以获取类的 KClass 对象,然后可以通过 qualifiedName 属性获取类的完整包名。例如:

    val packageName = MyClass::class.qualifiedName
    
  3. 获取类的属性:
    使用 memberProperties 属性可以获取类的所有属性,然后可以进一步获取属性的名称、类型、可见性等信息。

  4. 获取类的函数:
    使用 memberFunctions 属性可以获取类的所有函数,然后可以进一步获取函数的名称、参数、返回类型等信息。

  5. 获取类的构造函数:
    使用 constructors 属性可以获取类的所有构造函数,然后可以进一步获取构造函数的参数信息。

  6. 获取类的父类:
    使用 superclass 属性可以获取类的直接父类的 KClass 对象,然后可以递归查找父类的信息。

  7. 获取类的接口:
    使用 supertypes 属性可以获取类实现的所有接口的 KType 对象,然后可以进一步获取接口的信息。


1.1.8 kotlin反射与java反射比较

Kotlin 反射和 Java 反射在本质上都是用于在运行时检查、访问和修改程序的结构、属性和方法。它们都提供了动态处理类和对象的能力,但在细节和用法上有一些区别。

以下是 Kotlin 反射和 Java 反射的比较:

  1. 语法差异:
    Kotlin 反射的语法相对于 Java 反射来说更简洁和直观。在 Kotlin 中,可以通过 ::class 或者 ::functionName 的方式来获取 KClassKFunction 对象。而在 Java 中,需要使用 Class.forName() 来获取 Class 对象,或者通过 getDeclaredMethod() 等方法来获取 Method 对象。
  2. Null 安全:
    Kotlin 的类型系统天生支持 Null 安全,因此在 Kotlin 反射中,通过 KClassKFunction 等对象获取属性或方法时,编译器会自动处理 null 值和空安全。而在 Java 反射中,需要手动处理 null 值,容易引入空指针异常。
  3. 可空性处理:
    在 Kotlin 反射中,可以使用 findXXX 等方法来查找属性或方法,这些方法会返回可空类型,便于处理查找不存在的属性或方法。而在 Java 反射中,查找属性或方法时,如果不存在会抛出异常。
  4. 扩展属性和函数:
    Kotlin 反射支持扩展属性和函数,可以通过 KPropertyKFunction 对象来获取扩展属性和函数的信息。而 Java 反射不支持扩展属性和函数,只能获取类和对象的成员。
  5. Kotlin 特有功能:
    Kotlin 反射提供了一些 Kotlin 特有的功能,如获取 KVisibility(属性或函数的可见性)、获取内联函数和高阶函数的信息等。这些功能在 Java 反射中是不支持的。

以下是kotlin反射优缺点与java反射优缺点:

Kotlin 反射优点:

  1. 简洁语法:Kotlin 反射的语法相对于 Java 反射来说更简洁,直观,易于理解和使用。
  2. Null 安全:Kotlin 的类型系统天生支持 Null 安全,在 Kotlin 反射中处理空值更方便,避免了空指针异常。
  3. 扩展属性和函数:Kotlin 反射支持获取扩展属性和函数的信息,这是 Java 反射不具备的功能。
  4. Kotlin 特有功能:Kotlin 反射提供了一些特有的功能,例如获取属性或函数的可见性等,这些功能在 Java 反射中是不支持的。

Kotlin 反射缺点:

  1. 性能开销:反射是在运行时进行的,可能会带来一定的性能开销,特别是在频繁使用反射的情况下。
  2. 缺乏类型检查:Kotlin 反射缺乏编译时类型检查,容易引入类型错误和安全问题。
  3. 复杂性:对于不熟悉反射机制的开发者来说,可能会增加代码的复杂性和难以维护。

Java 反射优点:

  1. 可广泛使用:Java 反射是 Java 标准库的一部分,可以在任何 Java 程序中使用,无需额外导入其他库。
  2. 成熟稳定:Java 反射经过多年发展和使用,已经十分成熟和稳定,广泛应用于许多框架和库中。

Java 反射缺点:

  1. 冗长繁琐:Java 反射的语法相对繁琐,需要较多的代码和处理 null 值的检查。
  2. 空指针异常:Java 反射在处理 null 值时需要手动处理,容易引入空指针异常。
  3. 未提供 Kotlin 特性:Java 反射不支持获取 Kotlin 特有的功能,如扩展属性和函数等。

无论是 Kotlin 反射还是 Java 反射,在使用时都需要权衡其优缺点。反射是一种强大而灵活的工具,但也容易导致代码复杂性和性能问题。


1.1.9 kotlin反射获取泛型实参

在 Kotlin 中,通过反射可以获取泛型类型的实参信息。当一个类使用了泛型参数时,可以通过 KClass 对象来获取泛型参数的类型信息。

假设我们有一个泛型类 Box,它接受一个泛型参数 T,并且我们想要获取 Box 实例的泛型实参类型信息。

class Box<T>(val value: T)fun main() {val boxInt = Box(10)val boxString = Box("Hello")val classInt = boxInt::classval classString = boxString::class// 获取泛型实参类型信息if (classInt.typeParameters.isNotEmpty()) {val typeArg = classInt.typeParameters[0].upperBounds[0]println("boxInt 的泛型实参类型:$typeArg")}if (classString.typeParameters.isNotEmpty()) {val typeArg = classString.typeParameters[0].upperBounds[0]println("boxString 的泛型实参类型:$typeArg")}
}

在上述示例中,我们定义了一个 Box 类,它有一个泛型参数 T。在 main 函数中,我们创建了两个 Box 实例:boxInt 用于整数,boxString 用于字符串。然后,我们通过 boxInt::classboxString::class 获取它们的 KClass 对象。接着,我们使用 typeParameters 属性来获取泛型类型的参数信息,然后通过索引获取实参类型。

请注意,如果泛型类型在声明时没有指定上界(例如 class Box<T>),upperBounds 列表将会为空,因此需要在使用时进行判断。

总结起来,Kotlin 反射提供了获取泛型实参类型信息的能力,但由于泛型在编译时会进行类型擦除,因此在某些情况下可能需要谨慎处理,确保获取到的类型信息是正确的。


1.1.10 kotlin反射在Android中的使用

在 Android 开发中,Kotlin 反射可以在一些特定场景下发挥作用。虽然在 Android 中使用反射应该谨慎,因为会带来性能开销和潜在的安全问题,但以下是一些常见的使用场景:

  1. 反射获取资源ID:
    在 Android 中,有时候我们需要根据资源名称动态地获取资源的 ID。例如,我们可能有一个包含很多图片资源的文件夹,并且根据运行时的条件来动态地选择要加载的图片资源。
fun getDrawableResourceId(context: Context, resourceName: String): Int {return try {val packageName = context.packageNameval resId = context.resources.getIdentifier(resourceName, "drawable", packageName)if (resId == 0) {// 资源不存在时,处理错误逻辑// ...}resId} catch (e: Exception) {// 处理异常// ...0}
}

在上面的例子中,getDrawableResourceId 函数使用反射的方式根据资源名称动态获取对应的资源 ID。请注意,这里为了处理资源不存在或异常的情况,返回了一个默认值 0。

  1. 反射动态调用方法:
    有时候我们可能需要根据运行时的条件动态地调用不同的方法。通过反射,可以根据方法名动态调用类中的方法。
class Calculator {fun add(a: Int, b: Int): Int {return a + b}fun subtract(a: Int, b: Int): Int {return a - b}
}fun main() {val calculator = Calculator()val operation = "add" // 或者 "subtract"val methodName = "${operation.capitalize()}"try {val method = Calculator::class.java.getMethod(methodName, Int::class.java, Int::class.java)val result = method.invoke(calculator, 5, 3)println("Result of $operation: $result")} catch (e: Exception) {// 处理异常// ...}
}

在上面的例子中,我们有一个 Calculator 类,它有两个方法 addsubtract。在 main 函数中,我们根据运行时的条件来动态调用不同的方法。这里使用了 getMethod() 方法来获取方法对象,然后通过 invoke() 方法调用该方法。

kotlin反射在Android中的其他使用场景

  1. 反射注解处理器:
    在 Android 中,我们可以使用 Java 的反射来创建自定义的注解处理器。以下是一个简单的例子:
// 自定义注解
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
annotation class MyAnnotation// 注解处理器
class MyAnnotationProcessor : AbstractProcessor() {override fun getSupportedAnnotationTypes(): Set<String> {return setOf(MyAnnotation::class.java.canonicalName)}override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment?): Boolean {if (annotations != null) {for (element in roundEnv?.getElementsAnnotatedWith(MyAnnotation::class.java) ?: emptySet()) {// 对使用 MyAnnotation 注解的类进行处理,生成新的代码或进行其他操作// ...}}return true}
}

在这个例子中,我们首先定义了一个自定义的注解 MyAnnotation,然后创建了一个注解处理器 MyAnnotationProcessor,它继承自 AbstractProcessor。在 getSupportedAnnotationTypes() 方法中,我们指定该注解处理器支持处理 MyAnnotation 注解。在 process() 方法中,我们可以获取使用了 MyAnnotation 注解的类元素,并对这些类进行处理。

  1. 动态加载类:
    使用反射可以在运行时动态加载类,例如从远程服务器下载类文件并实例化。
fun loadClass(className: String): Any? {return try {val clazz = Class.forName(className)val instance = clazz.newInstance()instance} catch (e: ClassNotFoundException) {// 处理类不存在的情况// ...null} catch (e: InstantiationException) {// 处理实例化异常// ...null} catch (e: IllegalAccessException) {// 处理访问权限异常// ...null}
}

在这个例子中,loadClass 函数可以根据类名动态加载类并实例化它。请注意,这里的类名是完整的类路径,包括包名。

  1. 使用第三方库和插件:
    第三方库或插件可能使用反射来扩展或自定义应用的行为。例如,一些插件可能通过反射来获取应用中的类、方法或资源,并进行相关操作。
// 假设某个第三方插件提供了以下方法
fun performPluginAction(className: String, methodName: String) {try {val clazz = Class.forName(className)val method = clazz.getMethod(methodName)method.invoke(null)} catch (e: Exception) {// 处理异常// ...}
}

在这个例子中,我们假设第三方插件提供了一个方法 performPluginAction,该方法可以根据类名和方法名来调用应用中的方法,从而实现插件的自定义功能。


1.2 kotlin注解

1.2.1 kotlin注解概念和常见使用场景

在 Kotlin 中,注解(Annotations)是一种用于提供元数据(metadata)的特殊标记。它们不直接影响程序的运行,而是提供关于程序元素(类、函数、属性等)的附加信息。注解通常用于在编译时或运行时处理代码,比如代码生成、静态分析、依赖注入等。

Kotlin 注解的声明类似于 Java 注解,使用 @ 符号紧跟着注解名称,放在目标元素的前面。例如:

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotation

这个例子定义了一个名为 MyAnnotation 的注解。在 @Target 中,我们指定了该注解可以标记的目标元素,这里是类和函数。而 @Retention 则用来指定该注解在编译后是否保留到运行时(RUNTIME),还是仅在编译时(SOURCE)或类加载时(CLASS)保留。

常见的 Kotlin 注解使用场景包括:

  1. 代码生成: 注解可以用于在编译时生成额外的代码。比如,通过注解指示某个类或函数需要实现特定接口,编译器可以自动生成相应的代码。
  2. 依赖注入: 注解在依赖注入框架中广泛使用。通过使用注解,可以标记需要注入的依赖项或指示框架如何创建和管理依赖项。
  3. 序列化/反序列化: 注解可以用于在对象和 JSON 之间进行映射,这在处理网络请求或持久化数据时非常有用。
  4. 单元测试: 在单元测试中,我们可以使用注解标记要测试的特定方法,以便测试框架可以找到并执行这些测试。
  5. 数据验证: 注解可以用于验证数据的正确性,比如检查字段是否为空、长度是否符合要求等。
  6. Android 开发: 在 Android 开发中,注解经常用于标记 Activity、Fragment 或权限请求等。
  7. Spring 框架: Spring 中的注解大量用于声明服务、控制器和依赖注入等。

以上仅是一些常见的使用场景,实际上注解的应用是非常广泛的,开发者也可以根据需求定义自己的注解。要使用注解,你需要了解如何声明和定义注解,并使用相应的处理器(annotation processor)来处理这些注解。


1.2.2 kotlin注解常见用法

Kotlin 注解的常见用法包括以下几种:

  1. 标记类、函数或属性: 最基本的用法是将注解应用于类、函数或属性上,以标记它们具有特定的特性或行为。例如,可以创建一个注解来标记实体类,然后在数据类上使用该注解。
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Entity@Entity
data class User(val id: Int, val name: String)
  1. 代码生成: 注解可以用于在编译时生成额外的代码。通过定义注解和相应的处理器,可以在编译阶段生成一些重复性的代码,简化开发流程。
  2. 依赖注入: 在依赖注入框架中,注解用于标记需要注入的依赖项或配置依赖项的方式。例如,在使用 Dagger 或 Koin 进行依赖注入时,通常使用注解来指示依赖项。
@Module
class AppModule {@Provides@Singletonfun provideApiService(): ApiService {return ApiService()}
}
  1. 序列化/反序列化: 注解可以用于在对象和 JSON 之间进行映射,用于数据的序列化和反序列化。常见的库如 Gson 或 kotlinx.serialization 都使用注解来配置序列化过程。
@Serializable
data class User(val id: Int, val name: String)// Serialization
val jsonString = Json.encodeToString(User(1, "John"))// Deserialization
val user = Json.decodeFromString<User>(jsonString)
  1. 单元测试: 在单元测试中,注解可以用来标记要测试的方法或设置测试环境。
@Test
fun testAddition() {val result = add(2, 3)assertEquals(5, result)
}
  1. 数据验证: 注解可以用于验证数据的正确性。通过定义验证注解和对应的验证器,可以方便地对数据进行验证。
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class Range(val min: Int, val max: Int)data class Person(@Range(min = 1, max = 120)val age: Int
)
  1. Android 开发: 在 Android 开发中,注解广泛用于标记 Activity、Fragment 或权限请求等。
  2. Spring 框架: 在 Spring 中,注解用于声明服务、控制器和依赖注入等。

这些只是 Kotlin 注解的一些常见用法,实际上你可以根据具体需求和创造力,定义并使用注解来满足更多的编程场景。


1.2.3 kotlin注解的声明

在 Kotlin 中,声明注解需要使用 annotation class 关键字。注解声明包含注解名称、可选的构造函数和其他注解用于配置该注解的行为。下面是一个简单的注解声明示例:

annotation class MyAnnotation

这个例子声明了一个名为 MyAnnotation 的注解。可以在其他地方使用这个注解来标记类、函数、属性等。

为了为注解添加更多配置选项,可以在注解声明中添加属性,并在构造函数中为这些属性提供默认值。例如:

annotation class MyAnnotation(val name: String,val priority: Int = 0,val enabled: Boolean = true
)

在这个例子中,我们在 MyAnnotation 注解中定义了三个属性:namepriorityenabled。其中,priorityenabled 属性具有默认值,因此在使用该注解时可以不提供这些属性的值。如果需要提供属性的值,可以在注解使用时指定,如下所示:

@MyAnnotation(name = "Example", priority = 2, enabled = false)
class MyClass {// Class body
}

在上面的示例中,我们使用 MyAnnotation 注解标记了一个名为 MyClass 的类,并为注解的属性 namepriorityenabled 提供了具体的值。

除了在注解声明中定义属性,我们还可以使用元注解来为注解本身添加一些元数据。元数据可以包括该注解可以应用于的目标元素类型(类、函数、属性等)以及注解的生命周期(编译时、运行时等)等信息。例如:

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotation

在这个例子中,我们使用 @Target@Retention 元注解来配置 MyAnnotation 注解的目标和生命周期。

这就是 Kotlin 中声明注解的基本语法和一些高级用法。声明注解是使用 Kotlin 注解的第一步,接下来你可以根据需求进一步定义处理器来处理这些注解,以实现不同的功能。


1.2.4 kotlin注解的应用

当我们定义一个 Kotlin 注解时,我们使用 annotation class 关键字,后跟注解的名称。然后,我们可以在注解中定义属性(可选),用于配置注解的行为。

下面是一个完整的 Kotlin 注解定义和使用的示例:

// 定义一个注解 MyAnnotation
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotation(val name: String, val priority: Int = 0)// 使用注解 MyAnnotation
@MyAnnotation(name = "Example", priority = 2)
class MyClass {@MyAnnotation(name = "Function", priority = 1)fun myFunction() {// 函数体}
}

在上面的示例中,我们首先定义了一个名为 MyAnnotation 的注解,并在注解中声明了两个属性:namepriority。其中,priority 属性有一个默认值为 0,因此在使用该注解时可以不提供 priority 的值。接着,我们在 MyClass 类和 myFunction 方法上应用了这个注解,并为属性 namepriority 分别提供了具体的值。

请注意,在注解声明之前的 @Target@Retention 注解是元注解,用于配置 MyAnnotation 注解本身的属性。@Target 指定了该注解可以应用于的目标元素类型,这里是类和函数。@Retention 指定了该注解在编译后是否保留到运行时。

让我们来实现一个简单的路由功能。我们将定义一个注解 @Route,用于标记希望作为路由的 Activity 类。然后,我们会实现一个简单的路由管理器,它能够根据路由信息启动相应的 Activity。

首先,我们定义注解 @Route

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Route(val path: String)

接下来,我们创建一些 Activity 并标记它们为路由:

@Route("/home")
class HomeActivity : AppCompatActivity() {// ...
}@Route("/settings")
class SettingsActivity : AppCompatActivity() {// ...
}@Route("/profile")
class ProfileActivity : AppCompatActivity() {// ...
}

现在,我们实现一个简单的路由管理器,它将根据路由信息启动相应的 Activity:

object Router {private val routes: MutableMap<String, Class<out Activity>> = mutableMapOf()fun registerRoute(path: String, activityClass: Class<out Activity>) {routes[path] = activityClass}fun navigateTo(activity: Activity, path: String) {val activityClass = routes[path]if (activityClass != null) {val intent = Intent(activity, activityClass)activity.startActivity(intent)} else {Toast.makeText(activity, "Route not found!", Toast.LENGTH_SHORT).show()}}
}

Router 中,我们使用 routes 存储路由信息,并提供 registerRoute 方法用于注册路由。然后,我们可以通过 navigateTo 方法根据路由信息启动相应的 Activity。

现在,我们在应用程序的入口处注册路由并根据路由信息启动 Activity:

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 注册路由Router.registerRoute("/home", HomeActivity::class.java)Router.registerRoute("/settings", SettingsActivity::class.java)Router.registerRoute("/profile", ProfileActivity::class.java)// 启动 ActivityfindViewById<Button>(R.id.btn_home).setOnClickListener {Router.navigateTo(this, "/home")}findViewById<Button>(R.id.btn_settings).setOnClickListener {Router.navigateTo(this, "/settings")}findViewById<Button>(R.id.btn_profile).setOnClickListener {Router.navigateTo(this, "/profile")}}
}

在上面的代码中,我们在 MainActivity 中注册了几个路由,并在按钮点击时调用 navigateTo 方法启动相应的 Activity。

这个例子展示了一个稍微复杂的案例,其中使用了自定义注解、路由管理器和 Activity 的启动。实际应用中,可以根据需要扩展路由管理器以支持更多的功能和场景。


1.2.5 Kotlin中的元注解

1.2.5.1 @Target元注解

在 Kotlin 中,元注解(meta-annotations)是用于注解其他注解的注解。其中,@Target 是 Kotlin 提供的一个元注解之一。@Target 用于指定自定义注解可以应用于哪些元素类型,比如类、函数、属性等。通过 @Target,我们可以限制自定义注解的使用范围,从而确保注解被正确地应用在合适的元素上。

下面是 @Target 元注解的源码定义和使用示例:

// @Target 元注解的源码定义
@Target(allowedTargets: AnnotationTarget)// 示例:定义一个注解并使用 @Target 元注解指定其适用范围
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotation

@Target 元注解的源码定义中,我们可以看到它接收一个参数 allowedTargets,它的类型是 AnnotationTarget 枚举类。AnnotationTarget 是 Kotlin 中用于表示注解适用范围的枚举,它包含了多个元素类型,例如 CLASSFUNCTIONPROPERTYFIELD 等。

在我们的示例中,我们定义了一个名为 MyAnnotation 的注解,并在 @Target 元注解中使用了 AnnotationTarget.CLASSAnnotationTarget.FUNCTION,这意味着 MyAnnotation 注解只能应用在类和函数上,不能应用在其他元素类型上。

接下来,我们来看看如何使用 MyAnnotation 注解:

@MyAnnotation
class MyClass {@MyAnnotationfun myFunction() {// 函数体}// 下面的注解是不合法的,因为 MyAnnotation 不适用于属性// @MyAnnotation// val myProperty: Int = 0
}

在上面的示例中,我们将 MyAnnotation 注解应用在了 MyClass 类和 myFunction 函数上,而注解应用在 myProperty 属性上会导致编译错误,因为我们在 @Target 元注解中限制了 MyAnnotation 的适用范围为类和函数,而不包括属性。

通过使用 @Target 元注解,我们可以明确地控制自定义注解的使用范围,从而避免误用或不正确地应用注解。这有助于保持代码的清晰性和准确性。


1.2.5.2 @Retention元注解

在 Kotlin 中,元注解(meta-annotations)是用于注解其他注解的注解。@Retention 是 Kotlin 提供的另一个元注解,它用于指定自定义注解在编译后的保留策略,即注解的生命周期。@Retention 可以帮助我们控制注解是否在编译后保留到运行时,以及是否可以通过反射在运行时获取注解的信息。

下面是 @Retention 元注解的源码定义和使用示例:

// @Retention 元注解的源码定义
@Retention(AnnotationRetention)// 示例:定义一个注解并使用 @Retention 元注解指定其生命周期
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotation

@Retention 元注解的源码定义中,我们可以看到它接收一个参数 AnnotationRetention,它是一个枚举类,用于表示注解的保留策略。AnnotationRetention 包含三个枚举常量:SOURCEBINARYRUNTIME,分别代表了注解在编译后的不同生命周期。

  1. SOURCE: 注解仅保留在源代码中,在编译后不会包含在生成的类文件中,也不会保留到运行时。
  2. BINARY: 注解保留在编译后的类文件中,但在运行时不可访问。
  3. RUNTIME: 注解在编译后保留到运行时,可以通过反射在运行时获取注解的信息。

在我们的示例中,我们在 @Retention 元注解中使用了 AnnotationRetention.RUNTIME,这意味着 MyAnnotation 注解会在编译后保留到运行时,可以通过反射在运行时获取注解的信息。

接下来,我们来看看如何使用 MyAnnotation 注解:

@MyAnnotation
class MyClass {@MyAnnotationfun myFunction() {// 函数体}
}

在上面的示例中,我们将 MyAnnotation 注解应用在了 MyClass 类和 myFunction 函数上。由于我们在 @Retention 元注解中指定了 AnnotationRetention.RUNTIME,所以 MyAnnotation 注解会在编译后保留到运行时,我们可以通过反射来获取它们的信息。

通过使用 @Retention 元注解,我们可以控制自定义注解的生命周期,选择注解在编译后是否保留到运行时,以及是否可以通过反射获取注解的信息。这为我们提供了更多的灵活性和功能来定义和使用注解。


1.2.5.3 @MustBeDocumented元注解

在 Kotlin 中,@MustBeDocumented 是一个元注解,它用于指示被注解的注解应该被文档化。元注解本身并不直接影响代码的运行,而是提供了有关注解的额外信息,以帮助开发者了解如何正确使用该注解。

当我们使用 @MustBeDocumented 元注解标记一个注解时,这个注解会被 JavaDoc 或其他文档生成工具所识别,使得在生成 API 文档时可以包含有关该注解的说明信息。这样,开发者在查看文档时就能了解到有关该注解的使用方式、含义和限制等相关信息。

下面是 @MustBeDocumented 元注解的源码定义和使用示例:

// @MustBeDocumented 元注解的源码定义
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class MustBeDocumented// 示例:定义一个被 @MustBeDocumented 元注解标记的注解
@MustBeDocumented
annotation class MyAnnotation

在上面的示例中,我们定义了一个名为 MyAnnotation 的注解,并在其上使用了 @MustBeDocumented 元注解。MyAnnotation 是一个普通注解,没有特别的功能,但是由于我们在其上使用了 @MustBeDocumented,它会被文档化,因此在生成 API 文档时可以包含关于该注解的说明。

在使用注解时,我们可以像使用其他注解一样,将 MyAnnotation 应用到类、函数或属性上:

@MyAnnotation
class MyClass {@MyAnnotationfun myFunction(@MyAnnotation param: String): String {return "Hello, $param"}
}

在上面的示例中,我们将 MyAnnotation 注解应用在了 MyClass 类、myFunction 函数和函数的参数 param 上。由于我们在 MyAnnotation 注解上使用了 @MustBeDocumented 元注解,因此在生成 API 文档时,可以包含有关 MyAnnotation 注解的相关说明。

总结:@MustBeDocumented 元注解用于标记被注解的注解应该被文档化。它对代码本身没有任何影响,但可以帮助开发者在生成 API 文档时提供有关注解的说明信息,使得文档更加丰富和易于理解。


1.2.5.4 @Repeatable元注解

在 Kotlin 中,@Repeatable 是一个元注解,它用于指示被注解的注解可以在同一个元素上多次重复使用。在 Kotlin 1.1 版本引入之前,Java 中的注解是不支持多次重复使用的,因此 Kotlin 引入了 @Repeatable 元注解来解决这个问题。

使用 @Repeatable 元注解后,我们可以将同一个注解多次应用于同一个元素,而无需创建一个包含多个相同注解的数组。这样能够提高代码的可读性和简洁性,使得注解的使用更加灵活。

下面是 @Repeatable 元注解的源码定义和使用示例:

// @Repeatable 元注解的源码定义
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Repeatable// 示例:定义一个可重复使用的注解和使用
@Repeatable
annotation class Tag(val name: String)@Tag(name = "Kotlin")
@Tag(name = "Programming")
class MyClass {// ...
}

在上面的示例中,我们首先定义了一个名为 Tag 的注解,并在其上使用了 @Repeatable 元注解。Tag 注解是一个可重复使用的注解,这意味着我们可以在同一个元素(例如类、函数、属性等)上多次使用 Tag 注解,而不需要使用数组。

接下来,我们将 Tag 注解应用在 MyClass 类上,并在注解中提供多个不同的 name 属性值。这样,MyClass 类就被标记为同时具有两个 Tag

请注意,要让 @Repeatable 生效,需要确保元注解 @Target 的目标类型为 AnnotationTarget.ANNOTATION_CLASS,并且注解本身的声明与使用都是按照上面的示例进行的。

在实际使用中,可重复使用的注解可以帮助我们更方便地组织元数据,并提高代码的可读性。然而,需要注意的是,如果你需要在 Kotlin 代码中与使用 Java 中的可重复注解进行交互,可能需要特别处理,因为 Kotlin 和 Java 在处理重复注解上存在一些差异。


1.2.6 Kotlin中的预置注解

在 Kotlin 中,预置注解(Built-in Annotations)是一些特殊的注解,用于与 Java 交互或控制 Kotlin 代码的编译和行为。下面是这些预置注解的作用和使用示例:

  1. @JvmDefault: 用于在接口中声明默认方法,这样 Kotlin 接口可以与 Java 8+ 的接口进行互操作。
interface MyInterface {@JvmDefaultfun doSomething() {// Default implementation}
}
  1. @JvmField: 用于将 Kotlin 属性暴露为公共字段,使得该属性可以像 Java 字段一样直接访问。
class MyClass {@JvmFieldvar myField: Int = 42
}
  1. @JvmMultifileClass: 用于将多个 Kotlin 文件合并为一个 Java 类,通常用于在不同文件中定义同一个类的扩展函数或属性。
// File MyClass.kt
@file:JvmMultifileClass
class MyClass// File MyClassExtensions.kt
@file:JvmName("MyClassExtensions")
fun MyClass.myExtensionFunction() {// Extension function implementation
}
  1. @JvmName: 用于修改生成的 Java 类或方法的名称。
@file:JvmName("MyUtils")fun myUtilityFunction() {// Function implementation
}
  1. @JvmOverloads: 用于生成多个重载函数,从而允许 Java 代码调用 Kotlin 函数时使用不同数量的参数。
@JvmOverloads
fun myFunction(a: Int, b: Int = 0, c: Int = 0) {// Function implementation
}
  1. @JvmPackageName: 用于指定生成的 Java 类的包名。
@file:JvmPackageName("com.example.utils")class MyClass {// Class implementation
}
  1. @JvmStatic: 用于将 Kotlin 伴生对象中的函数或属性声明为 Java 的静态成员。
class MyClass {companion object {@JvmStaticfun staticFunction() {// Static function implementation}}
}
  1. @JvmSuppressWildcards@JvmWildcard: 用于在泛型类型中消除类型通配符。
fun myFunction(list: List<@JvmSuppressWildcards String>) {// Function implementation
}
  1. @JvmSynthetic: 用于将 Kotlin 文件中生成的额外的合成方法标记为 synthetic(合成)。
@JvmSynthetic
fun myInternalFunction() {// Function implementation
}
  1. @Throws: 用于声明一个函数可能会抛出指定的异常。
@Throws(IOException::class)
fun myFunction() {// Function implementation
}
  1. @Transient: 用于指示属性在序列化过程中应该被忽略。
class MyClass {@Transientvar myTransientField: String = "Data"
}
  1. @Strictfp: 用于声明一个类或方法应该遵循 Java 的严格浮点计算规则。
@Strictfp
class MyStrictfpClass {// Class implementation
}
  1. @Synchronized: 用于声明一个方法应该在调用时进行同步,避免并发访问问题。
class MyThreadSafeClass {@Synchronizedfun synchronizedMethod() {// Synchronized method implementation}
}
  1. @Volatile: 用于在多线程环境中声明一个属性应该是 volatile 类型,确保多个线程之间的可见性。
class MyVolatileClass {@Volatilevar flag: Boolean = false
}

这些预置注解提供了很多与 Java 交互和代码控制的功能,可以帮助我们更好地在 Kotlin 和 Java 之间进行无缝的互操作。在使用这些注解时,需要根据具体的需求和场景来选择合适的注解。


1.2.7 kotlin注解与java注解比较

Kotlin 注解与 Java 注解在很多方面是类似的,因为 Kotlin 是建立在 Java 平台上的,并且支持 Java 的注解机制。但是,它们之间还是有一些区别和特点的。

相同点:

  1. 注解的声明方式: 在 Kotlin 和 Java 中,都可以使用相似的语法来声明注解。
  2. 元注解: Kotlin 和 Java 都支持元注解,用于注解其他注解。
  3. 用途和功能: 注解的主要目的是为了在代码中添加元数据,用于对代码进行标记、配置或提供额外的信息。

不同点:

  1. 注解的声明关键字: 在 Java 中,注解的声明关键字是 @interface,而在 Kotlin 中,是 annotation class
  2. 可空性: Kotlin 中的注解的属性默认是可空的,需要使用 null 来表示空值。而在 Java 中,注解的属性不支持可空性,没有默认值的属性必须在使用注解时进行赋值。
  3. 函数作为参数: Kotlin 中的注解支持将函数作为参数传递,这在 Java 注解中是不支持的。
  4. 默认值: Kotlin 注解的属性可以有默认值,而在 Java 注解中,只有常量属性才能有默认值。
  5. 使用方式: 在 Java 中,注解可以应用于类、方法、字段、参数等多种元素上。而在 Kotlin 中,由于某些 Java 注解的使用方式不符合 Kotlin 的习惯,部分 Java 注解在 Kotlin 中的使用受到限制,如 @Repeatable
  6. 元注解的限制: 在 Java 中,元注解的目标可以是 ANNOTATION_TYPECONSTRUCTORFIELDLOCAL_VARIABLEMETHODPACKAGETYPE。而在 Kotlin 中,元注解的目标仅可以是 ANNOTATION_CLASS

总体而言,Kotlin 和 Java 的注解机制在很多方面是类似的,但 Kotlin 在语法和功能上进行了一些改进和增强。这使得 Kotlin 注解更加灵活和强大,并且能够更好地与 Java 代码进行交互。在 Kotlin 中,可以充分利用 Java 注解提供的丰富生态系统,并结合 Kotlin 的特性,使得代码更加简洁和易于理解。

kotlin注解优缺点与java注解优缺点

Kotlin 注解优点

  1. Nullability: Kotlin 注解支持属性的可空性,可以在注解的属性上使用 null 表示空值,使得注解的使用更加灵活和方便。
  2. 默认值: Kotlin 注解的属性可以有默认值,这样在使用注解时可以只设置需要的属性,减少了冗余代码。
  3. 函数作为参数: Kotlin 注解支持将函数作为参数传递,这在某些场景下非常有用,可以实现更加复杂的配置和处理。
  4. 可重复注解: Kotlin 支持使用 @Repeatable 元注解来定义可重复注解,简化了多个相同注解的使用。
  5. 更简洁的声明: Kotlin 的注解声明方式更加简洁,使用 annotation class 关键字,比 Java 的 @interface 更直观。

Kotlin 注解缺点

  1. 与 Java 交互限制: Kotlin 中的某些注解的使用受到与 Java 注解的交互限制,例如 @Repeatable 和某些元注解的目标类型限制。

Java 注解优点

  1. 广泛使用: Java 注解在 Java 生态系统中得到广泛使用,许多库和框架都使用了大量的注解,可以提供更丰富的元数据和功能。
  2. 与 Java 代码无缝交互: Java 注解在 Java 代码中可以无缝使用,并且许多第三方库和框架都使用了 Java 注解。

Java 注解缺点

  1. 不支持默认值: 在 Java 注解中,只有常量属性才能有默认值,而普通属性没有默认值。
  2. 不支持可空性: Java 注解的属性不支持可空性,无法使用 null 来表示空值,必须使用默认值来代替。
  3. 函数作为参数受限: 在 Java 注解中,函数作为参数传递相对受限,通常需要使用特殊的注解方式来实现类似功能。

综上所述,Kotlin 注解在很多方面比 Java 注解更加灵活和方便,提供了更多功能和特性。但是由于 Kotlin 和 Java 注解的一些差异,导致在一些特殊情况下可能会受到一些限制。在实际开发中,可以根据具体的需求和场景来选择合适的注解方式。在 Kotlin 中,可以充分利用 Java 注解提供的丰富生态系统,并结合 Kotlin 的特性,使得代码更加简洁和易于理解。


1.2.8 kotlin注解在Android中的使用

在 Android 开发中,Kotlin 注解可以被广泛用于各种场景,如依赖注入、视图绑定、路由导航、序列化和权限请求等。以下是一些常见的使用情况和相应的示例代码:

  1. 视图绑定(View Binding)

Kotlin 注解可以用于视图绑定,以消除 findViewById() 的冗余代码。

// 假设有一个布局文件 activity_main.xml 包含一个 TextView 元素,id 为 tv_hello
class MainActivity : AppCompatActivity() {// 使用 @BindView 注解将布局中的 TextView 绑定到 activity 的属性@BindView(R.id.tv_hello)lateinit var textView: TextViewoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// ButterKnife.bind(this) 可以绑定视图ButterKnife.bind(this)textView.text = "Hello, Kotlin!"}
}
  1. 依赖注入

Kotlin 注解可用于依赖注入框架(如 Dagger2)来标记和注入依赖项。

// 假设有一个依赖项需要注入
class UserRepository @Inject constructor(private val apiService: ApiService) {// ...
}// 使用 @Inject 注解告诉 Dagger2 在需要时自动提供 UserRepository 实例
  1. 序列化和反序列化

Kotlin 注解可以用于序列化和反序列化数据类。

// 使用 @Serializable 注解标记一个数据类,以支持 Kotlinx Serialization 库
@Serializable
data class User(val id: Int, val name: String, val email: String)// 使用 Kotlinx Serialization 库将对象序列化为 JSON 或从 JSON 反序列化为对象
val jsonString = Json.encodeToString(User(1, "John", "john@example.com"))
val user = Json.decodeFromString<User>(jsonString)
  1. 权限请求

Kotlin 注解可以用于简化 Android 运行时权限请求。

// 使用 @RequiresPermission 注解标记需要权限的方法
@RequiresPermission(Manifest.permission.CAMERA)
fun openCamera() {// 打开相机的逻辑
}
  1. 事件绑定

Kotlin 注解可以用于简化事件绑定,例如点击事件。

// 使用 @OnClick 注解将点击事件与方法绑定
@OnClick(R.id.btn_submit)
fun onSubmitButtonClicked() {// 处理按钮点击事件
}

以上示例展示了一些在 Android 中使用 Kotlin 注解的常见场景。Kotlin 注解可以提高代码的可读性和简洁性,同时也能与现有的 Java 注解和框架良好地交互。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/77851.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图像快速傅里叶变换的工业应用案例简介:图像自相关,背景纹理去除,旋转矫正,划痕检测

快速傅里叶变换是非常重要的数学分析工具&#xff0c;同时也是一种非常重要的信号处理方法。 下面借助Halcon商业图像处理库&#xff0c;介绍些工业应用案例&#xff0c;我们可以通过案例理解图像快速傅里叶变换的一些应用场景。 案例1&#xff1a;图像自相关性确定芯片间距 …

数据可视化:Matplotlib详解及实战

1 Matplotlib介绍 Matplotlib是Python中最常用的可视化工具之一,可以非常方便地创建海量类型的2D图表和一些基本的3D图表。 Matplotlib提供了一个套面向绘图对象编程的API接口&#xff0c;能够很轻松地实现各种图像的绘制&#xff0c;并且它可以配合Python GUI工具&#xff08;…

2023年08月IDE流行度最新排名

点击查看最新IDE流行度最新排名&#xff08;每月更新&#xff09; 2023年08月IDE流行度最新排名 顶级IDE排名是通过分析在谷歌上搜索IDE下载页面的频率而创建的 一个IDE被搜索的次数越多&#xff0c;这个IDE就被认为越受欢迎。原始数据来自谷歌Trends 如果您相信集体智慧&am…

小白电脑装机(自用)

几个月前买了配件想自己装电脑&#xff0c;结果最后无法成功点亮&#xff0c;出现的问题是主板上的DebugLED黄灯常亮&#xff0c;即DRAM灯亮。对于微星主板的Debug灯&#xff0c;其含义这篇博文中有说明。 根据另一篇博文&#xff0c;有两种可能。 我这边曾将内存条和主板一块…

全球出货量突破1.3亿:华为智能穿戴产品国内第一份额诞生

根据华为运动健康技术暨HDC运动健康分论坛的消息&#xff0c;华为运动健康军团首席执行官兼华为终端BG智能穿戴与运动健康产品线总裁张炜透露&#xff0c;华为智能穿戴产品的全球出货量已突破1.3亿&#xff0c;用户数量也超过4.5亿&#xff0c;并多年来一直在中国智能手表市场占…

每日一题——复杂链表的复制

复杂链表的复制 题目链接 思路 如果不考虑random指针的复制&#xff0c;仅仅复制单链表的结构还是简单的。只需要通过一个指针cur遍历原链表&#xff0c;再不断创建新节点尾插到newHead后即可。 但如果要考虑random指针的复制&#xff0c;那过程就复杂了。 有小伙伴会这样想…

基于Azure OpenAI Service 的知识库搭建实验⼿册

1.概要 介绍如何使⽤Azure OpenAI Service 的嵌⼊技术&#xff0c;创建知识库&#xff1b;以及创建必要的资源组和资源&#xff0c;包括 Form Recognizer 资源和 Azure 翻译器资源。在创建问答机器⼈服务时&#xff0c;需要使⽤已部署模型的 Azure OpenAI 资源、已存在的…

Kafka-Broker工作流程

kafka集群在启动时&#xff0c;会将每个broker节点注册到zookeeper中&#xff0c;每个broker节点都有一个controller&#xff0c;哪个controller先在zookeeper中注册&#xff0c;哪个controller就负责监听brokers节点变化&#xff0c;当有分区的leader挂掉时&#xff0c;contro…

初识MySQL数据库之用户管理

目录 一、用户管理 二、用户 1. 用户信息 2. 创建用户 3. 用户登录测试 4. 删除用户 5. 设置用户远端登录 6. 修改密码 6.1 修改当前用户的密码 6.2 root用户修改指定用户的密码 三、权限 1. 数据库中的各个权限含义 2. 给用户授权 3. 查看用户拥有权限 4. 授权…

iMX6ULL驱动开发 | 让imx6ull开发板支持usb接口FC游戏手柄

手边有一闲置的linux开发板iMX6ULL一直在吃灰&#xff0c;不用来搞点事情&#xff0c;总觉得对不住它。业余打发时间就玩起来吧&#xff0c;总比刷某音强。从某多多上买来一个usb接口的游戏手柄&#xff0c;让开发板支持以下它&#xff0c;后续就可以接着在上面玩童年经典游戏啦…

【使用bat脚本实现批量创建文件夹、批量复制文件至对应文件夹中】

使用bat脚本实现批量创建文件夹、批量复制文件至对应文件夹中 常用cmd命令 场景一&#xff1a;在指定位置批量创建文件夹 在桌面创建一个txt文件编写创建目录代码 //在桌面"五保户结算单"的文件夹下创建名称为"1张三"的文件夹 md E:\桌面\五保户结算单\…

【类和对象】日期类总结

日期类是我们学习类和对象这部分知识的常客&#xff0c;本篇博客我们就对日期类成员函数进行全面总结 目录 一、一览Date.h函数声明 二、Date.cpp逐部分实现 一、流插入与流提取运算符重载 二、日期之间比较大小相等运算符重载 1. > 2. 3. > 4. ! 5. <…

element+vue 之动态form

1.页面部分 <div v-for"(item,index) in formList" :key"index"><el-col :span"6" v-if"item.inputType0"><el-form-item :label"item.conditionName" :prop"item.conditionCode":rules"{req…

Abaqus 中最常用的子程序有哪些 硕迪科技

在ABAQUS中&#xff0c;用户定义的子程序是一种重要的构件&#xff0c;可以将其插入到Abaqus分析中以增强该软件的功能和灵活性。这些子程序允许用户在分析过程中添加自定义材料模型、边界条件、初始化、加载等特定操作&#xff0c;以便更精准地模拟分析中的现象和现象。ABAQUS…

二叉树迭代遍历

PS:以下代码均为C实现 1.二叉树前序遍历 力扣 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 class Solution { public:vector<int> preorderTraversal(TreeNode* root) {stack<TreeNode*> st;vector<int> str;TreeNode* curroot;whil…

简单认识ELK日志分析系统

一. ELK日志分析系统概述 1.ELK 简介 ELK平台是一套完整的日志集中处理解决方案&#xff0c;将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用&#xff0c; 完成更强大的用户对日志的查询、排序、统计需求。 好处&#xff1a; &#xff08;1&#xff09;提高安全…

【leetcode】394. 字符串解码

题目链接&#xff1a;力扣 给定一个经过编码的字符串&#xff0c;返回它解码后的字符串。 编码规则为: k[encoded_string]&#xff0c;表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。 你可以认为输入字符串总是有效的&#xff1b;输入字符串中没…

MySQL 主从复制

MySQL主从复制是一种数据复制技术&#xff0c;用于将一个MySQL数据库的数据实时复制到其他MySQL数据库&#xff0c;通常一个作为主数据库&#xff08;master&#xff09;&#xff0c;其他作为从数据库&#xff08;slave&#xff09; 基本工作原理&#xff1a; 主数据库记录所有…

RabbitMQ 教程 | 第10章 网络分区

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 DevO…

【云原生】K8S二进制搭建三:高可用配置

目录 一、部署CoreDNS二、配置高可用三、配置负载均衡四、部署 Dashboard 一、部署CoreDNS 在所有 node 节点上操作 #上传 coredns.tar 到 /opt 目录中 cd /opt docker load -i coredns.tar在 master01 节点上操作 #上传 coredns.yaml 文件到 /opt/k8s 目录中&#xff0c;部…