2016-03-02 15 views
16

Vì vậy, tôi sử dụng kotlin cho android, và khi lạm phát quan điểm, tôi có xu hướng làm những việc sau:Kotlin tính lười biếng và các giá trị thiết lập lại: a lười biếng đại biểu resettable

private val recyclerView by lazy { find<RecyclerView>(R.id.recyclerView) } 

Phương pháp này sẽ làm việc. Tuy nhiên, có một trường hợp trong đó nó sẽ lỗi ứng dụng. Nếu đây là một mảnh, và mảnh đi vào backstack, onCreateView sẽ được gọi một lần nữa, và phân cấp khung nhìn của đoạn sẽ tái tạo. Có nghĩa là, recyclerView khởi tạo lười biếng sẽ chỉ ra một chế độ xem cũ không còn tồn tại nữa.

Một giải pháp là như thế này:

private lateinit var recyclerView: RecyclerView 

Và khởi tất cả các thuộc tính bên trong onCreateView.

Câu hỏi của tôi là, có cách nào để đặt lại thuộc tính lười biếng để chúng có thể được khởi chạy lại không? Tôi thích việc khởi tạo thực tế được thực hiện ở đầu lớp, giúp giữ mã được tổ chức. Vấn đề cụ thể được tìm thấy trong câu hỏi này: kotlin android fragment empty recycler view after back

+0

Bạn đang tìm kiếm biến có thể thay đổi lười biếng mà bạn có thể khởi tạo ngầm nhưng cũng có thể đặt rõ ràng hoặc bạn đang tìm bộ nhớ tải mà bạn có thể tải lại? – mfulton26

+0

tôi muốn khởi động một cách lười biếng một thuộc tính với tùy chọn đặt lại nếu cần. Việc khôi phục trạng thái trước khi khởi tạo lần đầu tiên –

+0

bạn cần một đại biểu tùy chỉnh cho điều đó, nó tương đối dễ viết. Nếu kịch bản của bạn thường được sử dụng, điều này thậm chí có thể được thực hiện trong stdlib – voddan

Trả lời

17

Đây là một phiên bản nhanh chóng của một người lười biếng có thể đặt lại, nó có thể thanh lịch hơn và cần kiểm tra gấp đôi để đảm bảo an toàn luồng. Bạn cần một cái gì đó để quản lý (theo dõi) của các đại biểu lười biếng để bạn có thể gọi cho thiết lập lại, và sau đó những thứ có thể được quản lý và thiết lập lại. Điều này kết thúc tốt đẹp lazy() trong các lớp quản lý này.

Đây là những gì lớp cuối cùng của bạn sẽ trông giống như, như một ví dụ:

class Something { 
    val lazyMgr = resettableManager() 
    val prop1: String by resettableLazy(lazyMgr) { ... } 
    val prop2: String by resettableLazy(lazyMgr) { ... } 
    val prop3: String by resettableLazy(lazyMgr) { ... } 
} 

Sau đó, để làm cho lười biếng là tất cả quay trở lại giá trị mới vào lần sau chúng được truy cập:

lazyMgr.reset() // prop1, prop2, and prop3 all will do new lazy values on next access 

Việc triển khai lười cài đặt:

class ResettableLazyManager { 
    // we synchronize to make sure the timing of a reset() call and new inits do not collide 
    val managedDelegates = LinkedList<Resettable>() 

    fun register(managed: Resettable) { 
     synchronized (managedDelegates) { 
      managedDelegates.add(managed) 
     } 
    } 

    fun reset() { 
     synchronized (managedDelegates) { 
      managedDelegates.forEach { it.reset() } 
      managedDelegates.clear() 
     } 
    } 
} 

interface Resettable { 
    fun reset() 
} 

class ResettableLazy<PROPTYPE>(val manager: ResettableLazyManager, val init:()->PROPTYPE): Resettable { 
    @Volatile var lazyHolder = makeInitBlock() 

    operator fun getValue(thisRef: Any?, property: KProperty<*>): PROPTYPE { 
     return lazyHolder.value 
    } 

    override fun reset() { 
     lazyHolder = makeInitBlock() 
    } 

    fun makeInitBlock(): Lazy<PROPTYPE> { 
     return lazy { 
      manager.register(this) 
      init() 
     } 
    } 
} 

fun <PROPTYPE> resettableLazy(manager: ResettableLazyManager, init:()->PROPTYPE): ResettableLazy<PROPTYPE> { 
    return ResettableLazy(manager, init) 
} 

fun resettableManager(): ResettableLazyManager = ResettableLazyManager() 

Và một số xét nghiệm đơn vị để đảm bảo:

class Tester { 
    @Test fun testResetableLazy() { 
     class Something { 
      var seed = 1 
      val lazyMgr = resettableManager() 
      val x: String by resettableLazy(lazyMgr) { "x ${seed}" } 
      val y: String by resettableLazy(lazyMgr) { "y ${seed}" } 
      val z: String by resettableLazy(lazyMgr) { "z $x $y"} 
     } 

     val s = Something() 
     val x1 = s.x 
     val y1 = s.y 
     val z1 = s.z 

     assertEquals(x1, s.x) 
     assertEquals(y1, s.y) 
     assertEquals(z1, s.z) 

     s.seed++ // without reset nothing should change 

     assertTrue(x1 === s.x) 
     assertTrue(y1 === s.y) 
     assertTrue(z1 === s.z) 

     s.lazyMgr.reset() 

     s.seed++ // because of reset the values should change 

     val x2 = s.x 
     val y2 = s.y 
     val z2 = s.z 

     assertEquals(x2, s.x) 
     assertEquals(y2, s.y) 
     assertEquals(z2, s.z) 

     assertNotEquals(x1, x2) 
     assertNotEquals(y1, y2) 
     assertNotEquals(z1, z2) 

     s.seed++ // but without reset, nothing should change 

     assertTrue(x2 === s.x) 
     assertTrue(y2 === s.y) 
     assertTrue(z2 === s.z) 
    } 
} 
1

tôi có nhiệm vụ giống nhau, và đây là những gì tôi đã sử dụng:

import kotlin.properties.ReadOnlyProperty 
import kotlin.reflect.KProperty 

class SingletonLazy<T : Any>(val initBlock:() -> T, val clazz: Class<T>) { 
    operator fun <R> provideDelegate(ref: R, prop: KProperty<*>): ReadOnlyProperty<R, T> = delegate() 

    @Suppress("UNCHECKED_CAST") 
    private fun <R> delegate(): ReadOnlyProperty<R, T> = object : ReadOnlyProperty<R, T> { 
     override fun getValue(thisRef: R, property: KProperty<*>): T { 
      val hash = clazz.hashCode() 
      val cached = singletonsCache[hash] 
      if (cached != null && cached.javaClass == clazz) return cached as T 
      return initBlock().apply { singletonsCache[hash] = this } 
     } 
    } 
} 

private val singletonsCache = HashMap<Int, Any>() 

fun <T> clearSingleton(clazz: Class<T>) : Boolean { 
    val hash = clazz.hashCode() 
    val result = singletonsCache[hash] 
    if (result?.javaClass != clazz) return false 

    singletonsCache.remove(hash) 
    return true 
} 

inline fun <reified T : Any> singletonLazy(noinline block:() -> T): SingletonLazy<T> 
     = SingletonLazy(block, T::class.java) 

sử dụng:

val cat: Cat by singletonLazy { Cat() } 

fun main(args: Array<String>) { 
    cat 
    println(clearSingleton(Cat::class.java)) 
    cat // cat will be created one more time 
    println(singletonsCache.size) 
} 

class Cat { 
    init { println("creating cat") } 
} 

Tất nhiên, bạn có thể có các chiến lược lưu trong bộ nhớ đệm.

Các vấn đề liên quan