Matt’s Tidbits #45 – Creating a private token class

Matt’s Tidbits (69 Part Series)

1 Matt’s Tidbits #35 – A strategy for debugging complex unit test failures
2 Matt’s Tidbits #36 – How to get complete code coverage in unit tests for a nullable Kotlin “var”
65 more parts…
3 Matt’s Tidbits #37 – How to run apps on multiple devices simultaneously!
4 Matt’s Tidbits #38 – An elegant way of dealing with RxLifecycle lint warnings
5 Matt’s Tidbits #39 – Why writing tests is so important
6 Matt’s Tidbits #40 – RxJava doOnSubscribe() behavior explained
7 Matt’s Tidbits #41 – Converting a BehaviorRelay into a PublishRelay
8 Matt’s Tidbits #42 – The answer to the Ultimate Question of Life, the Universe, and Everything…
9 Matt’s Tidbits #43 – Making the most of your phone’s sensors
10 Matt’s Tidbits #44 – How to take great screenshots on Android
11 Matt’s Tidbits #45 – Creating a private token class
12 Matt’s Tidbits #46 – Scratch files
13 Matt’s Tidbits #47 – Finding list items by title in Espresso
14 Matt’s Tidbits #48 – Separating out search results for test code
15 Matt’s Tidbits #49 – What to do when your (Log)cat misbehaves
16 Matt’s Tidbits #50 – How to (inadvertently) initialize a Kotlin val twice
17 Matt’s Tidbits #51 – Easy stack traces
18 Matt’s Tidbits #52 – Formatting etiquette
19 Matt’s Tidbits #53 – First year in review
20 Matt’s Tidbits #54 – Caches, deployments, and IT – oh my!
21 Matt’s Tidbits #55 – Strange Breakpoint behavior with Rx schedulers
22 Matt’s Tidbits #56 – Be careful with @JvmOverloads
23 Matt’s Tidbits #57 – Improving RxJava stack traces
24 Matt’s Tidbits #58 – A strange issue when upgrading to Android Studio 3.6
25 Matt’s Tidbits #59 – Unexpected new lint failures
26 Matt’s Tidbits #60 – Cleanly handling null values in Kotlin
27 Matt’s Tidbits #61 – Getting out of a dependency cycle
28 Matt’s Tidbits #62 – When you’re feeling exhausted…
29 Matt’s Tidbits #63 – Some good news about AGP 3.6.2
30 Matt’s Tidbits #64 – Digging into some Ham…crest assertions
31 Matt’s Tidbits #65 – It’s time for proper support for JUnit 5
32 Matt’s Tidbits #66 – The magic of Kotlin’s “reified” keyword
33 Matt’s Tidbits #67 – Some tips for working from home
34 Matt’s Tidbits #68 – Testing RxJava Observables
35 Matt’s Tidbits #69 – Neat Kotlin field tricks
36 Matt’s Tidbits #70 – Cleaner diffs!
37 Matt’s Tidbits #71 – Trying to figure out what to say
38 Matt’s Tidbits #72 – Migrating from RxJava 2 -> 3
39 Matt’s Tidbits #73 – Deciding which Kotlin scope function to use
40 Matt’s Tidbits #74 – Resolving an interesting Kotlin <-> Java interop issue
41 Matt’s Tidbits #75 – Moving tidbits!
42 Matt’s Tidbits #76 – A long-overdue feature
43 Matt’s Tidbits #77 – It pays to star issues
44 Matt’s Tidbits #78 – A new build view
45 Matt’s Tidbits #79 – RxLifecycle woes
46 Matt’s Tidbits #80 – A mind-blowing Rx revelation
47 Matt’s Tidbits #81 – The importance of vacation
48 Matt’s Tidbits #82 – An unexpected Mockito limitation
49 Matt’s Tidbits #83 – Puzzling unit test failures
50 Matt’s Tidbits #84 – JVM Options, explained
51 Matt’s Tidbits #85 – Fixing build issues in Bitrise
52 Matt’s Tidbits #86 – Debugging Gradle files
53 Matt’s Tidbits #87 – Finding missing Android sources
54 Matt’s Tidbits #88 – Using patches
55 Matt’s Tidbits #89 – Automating focus time
56 Matt’s Tidbits #90 – All about dashboards (and another milestone!)
57 Matt’s Tidbits #91 – The power of sharing
58 Matt’s Tidbits #92 – A few neat IDE tidbits
59 Matt’s Tidbits #93 – Getting started with React Native
60 Matt’s Tidbits #94 – Writing safe enums in TypeScript
61 Matt’s Tidbits #95 – Defining constants in TypeScript
62 Matt’s Tidbits #96 – Cleaner Components with React Fragments
63 Matt’s Tidbits #97 – Running/Debugging individual Jest unit tests
64 Matt’s Tidbits #98 – The truth will set you free (unless you’re using JavaScript)
65 Matt’s Tidbits #99 – Handling null elegantly
66 Matt’s Tidbits #100 – All About Teams
67 Matt’s Tidbits #101 – How to write a good bug report
68 Matt’s Tidbits #102 – Debugging a perplexing view binding issue
69 Matt’s Tidbits #103 – Launching an app from a URL

Last time I shared a tip on how to take great screenshots on Android. This week I wanted to share an interesting story about trying to create a private token class and the solution I came up with.

First, a little bit of background. In the project I’m working on, there’s a setup like this:

As you may have noticed, there’s one problem with this code (see comment on line 19) — there’s currently no way to unregister the callback that gets passed into the LowLevelLibraryInterface because it’s created anonymously. Why would we want to unregister this? We may want to call stopSomething() for a variety of reasons — we want to cancel this due to direct user action, or, perhaps more critically, we called this from an activity/fragment that is being destroyed and we want to clean up after ourselves so we don’t leak any memory.

So, clearly we need some way to keep track of these callbacks and be able to retrieve them later. The first step will be to change the instantiation so it’s no longer anonymous, and, we’ll need to notify callers of startSomething with what callback corresponds to their request (so they don’t unregister a callback for someone else).

The simplest implementation I came up with was this:

In this example, I create a Token instance for each Callback that’s created and store a mapping between them in a HashMap. The Token that gets generated is then returned to the caller. Why not just return the callback to them? Because then they could theoretically call it themselves, which could lead to serious issues down the road. Additionally, I created a special type (Token) instead of using int/string/etc. — this is to prevent clients from being able to accidentally use a token with a value that might correspond to another invocation of this method.

This version is pretty good, but I still wasn’t completely happy with it. The Token class is publicly available, and anyone can create these, and they really have no reason to.

So, I tried creating a more locked-down version of the Token class — this time using Kotlin’s Sealed Classes feature.

This version contains several improvements, namely that only code in this file can construct instances of TokenImpl, although everyone is still able to see and use the Token type. There’s one problem with this solution though — Java code in the same package is still able to see and use the TokenImpl() constructor.

After some research and, honestly, the fortunate timing of reading this article by Jake Wharton, I discovered the @JvmSynthetic annotation. Unfortunately this annotation cannot be applied to constructors, but I did finally find a way to work around this:

In this version, I finally have what I’m looking for! Defining the class and constructor as private prevents them from being visible outside of this file. And, the create() method with internal visibility lets me call this method from any places in this module that can see the TokenImpl class (in this case, only the current file). Lastly, the @JvmSynthetic annotation hides the create() method from Java code so it can’t be called from there either.

I hope you learned something interesting and useful in this week’s tidbit! If you can think of a better/different/cleaner way to solve this problem (or know of an existing bug against Kotlin/the JVM for this issue), please let me know in the comments below! And, please follow me on Medium if you’re interested in being notified of future tidbits.

Interested in joining the awesome team here at Intrepid? We’re hiring!

This tidbit was discovered on November 26, 2019.

Matt’s Tidbits (69 Part Series)

1 Matt’s Tidbits #35 – A strategy for debugging complex unit test failures
2 Matt’s Tidbits #36 – How to get complete code coverage in unit tests for a nullable Kotlin “var”
65 more parts…
3 Matt’s Tidbits #37 – How to run apps on multiple devices simultaneously!
4 Matt’s Tidbits #38 – An elegant way of dealing with RxLifecycle lint warnings
5 Matt’s Tidbits #39 – Why writing tests is so important
6 Matt’s Tidbits #40 – RxJava doOnSubscribe() behavior explained
7 Matt’s Tidbits #41 – Converting a BehaviorRelay into a PublishRelay
8 Matt’s Tidbits #42 – The answer to the Ultimate Question of Life, the Universe, and Everything…
9 Matt’s Tidbits #43 – Making the most of your phone’s sensors
10 Matt’s Tidbits #44 – How to take great screenshots on Android
11 Matt’s Tidbits #45 – Creating a private token class
12 Matt’s Tidbits #46 – Scratch files
13 Matt’s Tidbits #47 – Finding list items by title in Espresso
14 Matt’s Tidbits #48 – Separating out search results for test code
15 Matt’s Tidbits #49 – What to do when your (Log)cat misbehaves
16 Matt’s Tidbits #50 – How to (inadvertently) initialize a Kotlin val twice
17 Matt’s Tidbits #51 – Easy stack traces
18 Matt’s Tidbits #52 – Formatting etiquette
19 Matt’s Tidbits #53 – First year in review
20 Matt’s Tidbits #54 – Caches, deployments, and IT – oh my!
21 Matt’s Tidbits #55 – Strange Breakpoint behavior with Rx schedulers
22 Matt’s Tidbits #56 – Be careful with @JvmOverloads
23 Matt’s Tidbits #57 – Improving RxJava stack traces
24 Matt’s Tidbits #58 – A strange issue when upgrading to Android Studio 3.6
25 Matt’s Tidbits #59 – Unexpected new lint failures
26 Matt’s Tidbits #60 – Cleanly handling null values in Kotlin
27 Matt’s Tidbits #61 – Getting out of a dependency cycle
28 Matt’s Tidbits #62 – When you’re feeling exhausted…
29 Matt’s Tidbits #63 – Some good news about AGP 3.6.2
30 Matt’s Tidbits #64 – Digging into some Ham…crest assertions
31 Matt’s Tidbits #65 – It’s time for proper support for JUnit 5
32 Matt’s Tidbits #66 – The magic of Kotlin’s “reified” keyword
33 Matt’s Tidbits #67 – Some tips for working from home
34 Matt’s Tidbits #68 – Testing RxJava Observables
35 Matt’s Tidbits #69 – Neat Kotlin field tricks
36 Matt’s Tidbits #70 – Cleaner diffs!
37 Matt’s Tidbits #71 – Trying to figure out what to say
38 Matt’s Tidbits #72 – Migrating from RxJava 2 -> 3
39 Matt’s Tidbits #73 – Deciding which Kotlin scope function to use
40 Matt’s Tidbits #74 – Resolving an interesting Kotlin <-> Java interop issue
41 Matt’s Tidbits #75 – Moving tidbits!
42 Matt’s Tidbits #76 – A long-overdue feature
43 Matt’s Tidbits #77 – It pays to star issues
44 Matt’s Tidbits #78 – A new build view
45 Matt’s Tidbits #79 – RxLifecycle woes
46 Matt’s Tidbits #80 – A mind-blowing Rx revelation
47 Matt’s Tidbits #81 – The importance of vacation
48 Matt’s Tidbits #82 – An unexpected Mockito limitation
49 Matt’s Tidbits #83 – Puzzling unit test failures
50 Matt’s Tidbits #84 – JVM Options, explained
51 Matt’s Tidbits #85 – Fixing build issues in Bitrise
52 Matt’s Tidbits #86 – Debugging Gradle files
53 Matt’s Tidbits #87 – Finding missing Android sources
54 Matt’s Tidbits #88 – Using patches
55 Matt’s Tidbits #89 – Automating focus time
56 Matt’s Tidbits #90 – All about dashboards (and another milestone!)
57 Matt’s Tidbits #91 – The power of sharing
58 Matt’s Tidbits #92 – A few neat IDE tidbits
59 Matt’s Tidbits #93 – Getting started with React Native
60 Matt’s Tidbits #94 – Writing safe enums in TypeScript
61 Matt’s Tidbits #95 – Defining constants in TypeScript
62 Matt’s Tidbits #96 – Cleaner Components with React Fragments
63 Matt’s Tidbits #97 – Running/Debugging individual Jest unit tests
64 Matt’s Tidbits #98 – The truth will set you free (unless you’re using JavaScript)
65 Matt’s Tidbits #99 – Handling null elegantly
66 Matt’s Tidbits #100 – All About Teams
67 Matt’s Tidbits #101 – How to write a good bug report
68 Matt’s Tidbits #102 – Debugging a perplexing view binding issue
69 Matt’s Tidbits #103 – Launching an app from a URL

原文链接:Matt’s Tidbits #45 – Creating a private token class

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容