Kotlin -> lateinit 和 lazy 详解

lateinitlazy 详解

核心区别

特性lateinitlazy
类型可变属性修饰符 var不可变属性委托 val
初始化时机手动显式初始化,随时可变首次访问时自动初始化,之后不可变
空安全非空类型,但初始值可缺失非空类型,保证有值
适用类型不能用于基本类型(Int, Boolean等)可用于任何类型
线程安全不保证线程安全默认线程安全SYNCHRONIZED模式
检查机制使用前需确保已初始化,否则抛异常自动处理初始化,不会抛出未初始化异常

lateinit 核心用法

// 声明
class User {
    lateinit var name: String
    
    fun initialize() {
        name = "John" // 手动初始化
    }
    
    fun greet() {
        if (::name.isInitialized) { // 检查是否已初始化
            println("Hello, $name")
        }
    }
}

最佳使用场景:

  • 依赖注入
  • Activity/Fragment中的视图绑定
  • 单元测试的setUp方法中
  • 需要推迟初始化但之后可能需要修改的属性

lazy 核心用法

// 基本用法
class User {
    val name: String by lazy { 
        println("Computing name...")
        "John" // 计算并返回初始值
    }
}

// 指定线程安全模式
val expensiveData: List<Data> by lazy(LazyThreadSafetyMode.PUBLICATION) {
    loadDataFromDatabase()
}

线程安全模式:

  • SYNCHRONIZED:默认模式,线程安全,只执行一次初始化
  • PUBLICATION:多线程可能执行多次,但只有第一个结果被使用
  • NONE:不保证线程安全,适用于单线程环境,性能最好

最佳使用场景:

  • 计算开销大的属性
  • 需要根据条件计算的只读属性
  • 单例模式实现
  • 配置项和缓存数据

核心实现原理

lateinit

  • 在字节码级别,不为属性分配默认值
  • 访问前不进行空检查
  • 使用前若未初始化,抛出UninitializedPropertyAccessException

lazy

  • 内部使用SynchronizedLazyImpl等实现类
  • 持有一个初始化器函数和一个存储结果的AtomicReference
  • 首次访问时执行初始化器并缓存结果

如何选择

  • 如果属性需要在初始化后修改,使用lateinit var
  • 如果属性是只读的且可以延迟计算,使用lazy val
  • 如果处理基本类型,只能使用lazy
  • 如果在多线程环境中,优先考虑lazy
<think>好的,我现在需要帮用户找到关于KotlinJPA的教程、示例详解。首先,用户提到的引用内容里有关于Kotlin JPA插件的信息,特别是引用[1]引用[3]。用户可能想知道如何在Kotlin中使用JPA,尤其是实体类的创建、Repository接口以及相关注解的使用。 首先,我应该回顾引用内容。引用[1]提到Kotlin JPA插件是基于no-arg插件,为带有@Entity等注解的类生成无参构造函数。这很重要,因为Kotlin中类默认是final的,而且构造函数可能需要参数,但JPA要求实体有无参构造函数。因此,需要说明如何配置插件依赖。 然后,引用[3]是关于Spring Data JPA的配置应用,包括实体映射、Repository接口的创建等。结合这两部分,用户可能需要一个整合KotlinSpring Data JPA的教程,涵盖从环境配置到实际操作的步骤。 接下来,我需要组织回答结构。先介绍Kotlin与JPA集成的基本概念,然后分步骤讲解环境配置、实体类创建、Repository接口、示例代码等。同时,要提到必要的插件依赖,如Kotlin JPA插件Spring Boot Starter Data JPA。 用户可能需要具体的代码示例,比如实体类如何用Kotlin编写,Repository接口的定义,以及如何在Service中使用。此外,要强调Kotlin的特性,比如数据类(data class)默认参数,同时结合JPA的要求,比如无参构造函数的处理。 还要注意可能的常见问题,比如为什么需要Kotlin JPA插件,以及如何解决实体类中的final问题。引用[1]提到Kotlin Spring插件会打开有特定注解的类,而JPA插件则处理无参构造函数,这些都需要在教程中解释清楚。 最后,生成相关问题,可能涉及性能优化、复杂查询、与其他框架对比等,帮助用户进一步学习。确保所有代码示例符合用户要求的格式,数学公式用$...$,独立公式用$$...$$,但在这个问题中可能数学公式较少,主要关注代码配置。</think>### Kotlin与JPA集成教程及示例详解 #### 一、环境配置与依赖 1. **添加依赖** 在Spring Boot项目中集成Kotlin与JPA,需引入以下依赖(以Gradle为例): ```kotlin plugins { kotlin("jvm") version "1.8.22" kotlin("plugin.spring") version "1.8.22" kotlin("plugin.jpa") version "1.8.22" // 关键:Kotlin JPA插件 } dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") // Spring Data JPA支持[^3] implementation("com.fasterxml.jackson.module:jackson-module-kotlin") // Kotlin序列化支持 runtimeOnly("com.h2database:h2") // 示例中使用H2内存数据库 } ``` 2. **插件作用** - **Kotlin JPA插件**:基于`no-arg`插件,为标注`@Entity`、`@Embeddable`、`@MappedSuperclass`的类生成无参构造函数[^1]。 - **Kotlin Spring插件**:自动将带有Spring注解(如`@Component`、`@Transactional`)的类方法设为`open`,解决Kotlin默认`final`的限制[^1]。 --- #### 二、实体类创建 JPA实体类需满足以下条件: 1. **使用数据类简化代码** ```kotlin @Entity data class User( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long? = null, val name: String, val email: String, @Temporal(TemporalType.TIMESTAMP) val createdAt: Date = Date() ) ``` - `@Id`标注主键,`@GeneratedValue`设置主键生成策略。 - `Kotlin JPA插件`会自动生成无参构造函数,即使数据类显式定义了带参构造函数[^1]。 2. **关系映射示例(一对多)** ```kotlin @Entity data class Order( @Id @GeneratedValue val id: Long? = null, val totalPrice: Double, @OneToMany(mappedBy = "order", cascade = [CascadeType.ALL]) val items: List<OrderItem> = emptyList() ) @Entity data class OrderItem( @Id @GeneratedValue val id: Long? = null, val productName: String, val quantity: Int, @ManyToOne @JoinColumn(name = "order_id") val order: Order? = null ) ``` --- #### 三、Repository接口 Spring Data JPA通过接口自动实现CRUD操作: ```kotlin @Repository interface UserRepository : JpaRepository<User, Long> { fun findByNameContaining(keyword: String): List<User> @Query("SELECT u FROM User u WHERE u.email LIKE %:domain") fun findByEmailDomain(@Param("domain") domain: String): List<User> } ``` - **自定义查询方法**:根据方法名自动生成查询(如`findByNameContaining`)。 - **JPQL查询**:使用`@Query`注解编写复杂查询。 --- #### 四、服务层示例 在Service中调用Repository: ```kotlin @Service class UserService( private val userRepository: UserRepository ) { fun createUser(name: String, email: String): User { return userRepository.save(User(name = name, email = email)) } fun searchUsers(keyword: String): List<User> { return userRepository.findByNameContaining(keyword) } } ``` --- #### 五、常见问题与优化 1. **Lazy Loading问题** Kotlin数据类的`equals/hashCode`可能触发Lazy Loading异常。解决方案: - 使用`open class`替代`data class`。 - 避免在`equals/hashCode`中包含关联实体。 2. **审计功能** 添加`@CreatedDate``@LastModifiedDate`: ```kotlin @MappedSuperclass @EntityListeners(AuditingEntityListener::class) abstract class BaseEntity { @CreatedDate lateinit var createdAt: LocalDateTime @LastModifiedDate lateinit var updatedAt: LocalDateTime } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值