Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin で改善する Androidアプリの品質

Avatar for Yuki Anzai Yuki Anzai
August 25, 2018
13k

Kotlin で改善する Androidアプリの品質

Avatar for Yuki Anzai

Yuki Anzai

August 25, 2018
Tweet

More Decks by Yuki Anzai

Transcript

  1. ιϑτ΢ΣΞͷ඼࣭ = ཁҼͷ૊Έ߹Θͤ • ֎త඼࣭ཁҼ • ιϑτ΢ΣΞ੡඼ʹͦͷੑ࣭͕͋Δ͔ͳ͍͔ΛϢʔβʔ͕ೝ ࣝͰ͖Δੑ࣭ • εϐʔυɺ࢖͍΍͢͞ɺ…

    • ಺త඼࣭ཁҼ • ιϑτ΢ΣΞ੡඼ʹ͍ͭͯܗ༰͞ΕΔͦΕҎ֎ͷੑ࣭ • ϞδϡʔϧੑɺಡΈ΍͢͞ɺ…
  2. ֎త඼࣭ཁҼͷಛʹॏཁͳ4ཁҼ • ਖ਼֬͞ʢcorrectnessʣ • ؤৎ͞ʢrobustnessʣ • ৴པੑ = ਖ਼֬͞ +

    ؤৎ͞ • ֦ுੑʢextendibilityʣ • ࠶ར༻ੑʢreusabilityʣ • Ϟδϡʔϧੑ = ֦ுੑ + ࠶ར༻ੑ
  3. ߲໨2 ਺ଟ͘ͷίϯετϥΫλύϥϝʔλʹ௚໘ͨ࣌͠ ʹ͸ϏϧμʔΛݕ౼͢Δ new NutritionFacts(240, 8, 100, 0, 35, 27);

    new NutritionFacts.Builder(240, 8) .calories(100) .sodium(35) .carbohydrate(27) .build(); NG OK
  4. ߲໨2 ਺ଟ͘ͷίϯετϥΫλύϥϝʔλʹ௚໘ͨ࣌͠ ʹ͸ϏϧμʔΛݕ౼͢Δ Kotlin class NutritionFacts( val servingSize: Int, val

    servings: Int, val calories: Int = 0, val fat: Int = 0, val sodium: Int = 0, val carbohydrate: Int = 0 ) NutritionFacts( 240, 8, calories = 100, sodium = 35, carbohydrate = 27 )
  5. AlertDialog( message = "࡟আ͠·͔͢ʁ", negativeButtonLabel = "͸͍", positiveButtonListener = {

    delete() } ) class AlertDialog( private val title: String? = null, private val message: String? = null, private val positiveButtonLabel: String? = null, private val positiveButtonListener: (() -> Unit)? = null, private val negativeButtonLabel: String? = null, private val negativeButtonListener: (() -> Unit)? = null ) NG Kotlin
  6. AlertDialog.Builder() .message("࡟আ͠·͔͢ʁ") .positiveButton("͸͍") { delete() } .build() class AlertDialog private

    constructor(…) { class Builder { … private var positiveButtonLabel: String? = null private var positiveButtonListener: (() -> Unit)? = null … fun positiveButton(label: String, listener: (() -> Unit)?): Builder { positiveButtonLabel = label positiveButtonListener = listener return this } … } } OK ෆมࣜΛߏ੒͢ΔύϥϝʔλશମΛηολʔͰड͚औΔ Kotlin
  7. ߲໨4 private ͷίϯετϥΫλͰΠϯελϯεԽෆՄ ೳΛڧ੍͢Δ ΦϒδΣΫτએݴ or τοϓϨϕϧؔ਺ fun util1(a: Int,

    b: Int): Int { … } public class UtilityClass { private UtilityClass() { } public static int util1(int a, int b) { … } } Kotlin
  8. ߲໨8 equals ΛΦʔόʔϥΠυ͢Δͱ͖͸Ұൠܖ໿ʹ ͕ͨ͠͏ public class MyClass { public boolean

    equals(MyClass o) { … } } class MyClass { override fun equals(o: MyClass): Boolean { … } } Java Ͱ͸ॻ͚ͯ͠·͏ Kotlin Ͱ͸ίϯύΠϧΤϥʔ
  9. ߲໨11 clone Λ஫ҙͯ͠ΦʔόʔϥΠυ͢Δ public class Object { … protected native

    Object clone() throws CloneNotSupportedException; } ʮΦϒδΣΫτͷίϐʔΛߦ͏ԿΒ͔ͷ୅ସखஈΛఏڙ͢Δ͔ɺ ΦϒδΣΫτͷෳ੡Λ୯ʹఏڙ͠ͳ͍ํ͕͓ͦΒ͘ݡ໌Ͱ͢ɻʯ Kotlin ͷ Any ʹ͸ clone() ͕ͳ͍ Kotlin
  10. ߲໨11 clone Λ஫ҙͯ͠ΦʔόʔϥΠυ͢Δ ʮΦϒδΣΫτͷίϐʔʹର͢Δ্ख͍ํ๏͸ɺίϐʔίϯετ ϥΫλ͔ίϐʔϑΝΫτϦΛఏڙ͢Δ͜ͱͰ͢ɻʯ data class ͷ copy() val

    donutsBook = Book("donuts", "Android") val eclairBook = donutsBook.copy(title = "eclair") Iterable ͷ֦ுؔ਺ toList() val list = listOf("donuts", "eclair") val list2 = list.toList() Kotlin
  11. data class Person( val name: String, val age: Int )

    : Comparable<Person> { override fun compareTo(other: Person): Int { age.compareTo(other.age).let { if (it != 0) { return it } } return name.compareTo(other.name) } } ߲໨12 Comparable ͷ࣮૷Λݕ౼͢Δ Kotlin ͷ਺஋ܕ,Boolean,String,Char͸ compareTo() ͕༻ ҙ͞Ε͍ͯΔ ೥ྸঢॱ → ໊લABCॱ Kotlin
  12. ߲໨12 Comparable ͷ࣮૷Λݕ౼͢Δ ඪ४ؔ਺Λ׆༻͢Δ data class Person( val name: String,

    val age: Int ) : Comparable<Person> { override fun compareTo(other: Person): Int { return compareValuesBy(this, other, { it.age }, { it.name }) } } ೥ྸঢॱ → ໊લABCॱ Kotlin
  13. ߲໨14 public ͷΫϥεͰ͸ɺpublic ͷϑΟʔϧυͰ͸ ͳ͘ɺΞΫηαʔϝιουΛ࢖͏ public class Person { private

    String name; private int age; … public String getName() { return name; } public void setName(String name) { this.name = name; } … } OK
  14. ߲໨14 public ͷΫϥεͰ͸ɺpublic ͷϑΟʔϧυͰ͸ ͳ͘ɺΞΫηαʔϝιουΛ࢖͏ ֎෦͔ΒͷϓϩύςΟΞΫηε͸ৗʹ getter setter ܦ༝ ͳͷͰɺݴޠͱͯ͠ΞΫηαʔϝιου͕ڧ੍͞Ε͍ͯΔ

    Kotlin class Person(name: String, age: Int) { var name: String = name set(value) { println("set : name = $value") field = value } … }
  15. ߲໨15 ՄมੑΛ࠷খݶʹ͢Δ • 1. ΦϒδΣΫτͷঢ়ଶΛมߋ͢ΔͨΊʹ͍͔ͳΔϝιου΋ ఏڙ͠ͳ͍ • 2. Ϋϥε͕֦ுͰ͖ͳ͍͜ͱΛอূ͢Δɻ •

    3. ͢΂ͯͷϑΟʔϧυΛ fi nal ʹ͢Δ • 4. ͢΂ͯͷϑΟʔϧυΛ private ʹ͢Δ • 5. Մมίϯϙʔωϯτʹର͢Δಠ઎తΞΫηεΛอূ͢Δɻ
  16. ߲໨15 ՄมੑΛ࠷খݶʹ͢Δ • 1. ΦϒδΣΫτͷঢ়ଶΛมߋ͢ΔͨΊʹ͍͔ͳΔϝιου΋ ఏڙ͠ͳ͍ • 2. Ϋϥε͕֦ுͰ͖ͳ͍͜ͱΛอূ͢Δɻ •

    3. ͢΂ͯͷϓϩύςΟΛ val ʹ͢Δ • 4. ՄมίϯϙʔωϯτͷϓϩύςΟΛ private ʹ͢Δ • 5. Մมίϯϙʔωϯτʹର͢Δಠ઎తΞΫηεΛอূ͢Δɻ Kotlin
  17. ߲໨16 ܧঝΑΓίϯϙδγϣϯΛબͿ ʮܧঝ͸ڧྗͰ͕͢ɺΧϓηϧԽΛഁͬͯ͠·͏ͷͰ໰୊͕͋ Γ·͢ɻʯ public class InstrumentedSet<E> extends HashSet<E> {

    private int addCount = 0; @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(@NotNull Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } } NG
  18. public class InstrumentedSet<E> implements Set<E> { private final Set<E> s;

    private int addCount = 0; public InstrumentedSet(Set<E> s) { this.s = s; } @Override public boolean add(E e) { addCount++; return s.add(e); } @Override public boolean addAll(@NotNull Collection<? extends E> c) { addCount += c.size(); return s.addAll(c); } @Override public int size() { return s.size(); } … } OK
  19. public class InstrumentedSet<E> implements Set<E> { private final Set<E> s;

    private int addCount = 0; public InstrumentedSet(Set<E> s) { this.s = s; } @Override public boolean add(E e) { addCount++; return s.add(e); } @Override public boolean addAll(@NotNull Collection<? extends E> c) { addCount += c.size(); return s.addAll(c); } @Override public int size() { return s.size(); } @Override public boolean isEmpty() { return s.isEmpty(); } @Override public boolean contains(Object o) { return s.contains(o); } @NotNull @Override public Iterator<E> iterator() { return s.iterator(); } @NotNull @Override public Object[] toArray() { return s.toArray(); } @NotNull @Override public <T> T[] toArray(@NotNull T[] a) { return s.toArray(a); } @Override public boolean remove(Object o) { return s.remove(o); } @Override public boolean containsAll(@NotNull Collection<?> c) { return s.contains(c); } @Override public boolean retainAll(@NotNull Collection<?> c) { return s.retainAll(c); } @Override public boolean removeAll(@NotNull Collection<?> c) { return s.removeAll(c); } @Override public void clear() { s.clear(); } } సૹͯ͠Δ͚ͩ
  20. class InstrumentedSet2<E>( private val s: MutableSet<E> ) : MutableSet<E> by

    s { private var addCount = 0 override fun add(element: E): Boolean { addCount++ return s.add(element) } override fun addAll(elements: Collection<E>): Boolean { addCount += elements.size return s.addAll(elements) } } Kotlin
  21. ߲໨17 ܧঝͷͨΊʹઃܭ͓ΑͼจॻԽ͢ΔɺͰͳ͚Ε ͹ܧঝΛېࢭ͢Δ public final class Point { public class

    Point { private Point(int x, int y) { … } public static Point of(int x, int y) { return new Point(x, y); } ΫϥεΛ fi nal ͱએݴ͢Δ ίϯετϥΫλΛ private ͔ύοέʔδϓϥΠϕʔτʹͯ͠ɺ୅ ΘΓʹ static ϑΝΫτϦʔϝιουΛ௥Ճ͢Δ
  22. ߲໨19 ܕΛఆٛ͢ΔͨΊ͚ͩʹΠϯλϑΣʔεΛ࢖༻ ͢Δ ఆ਺ͷम০Λճආ͢ΔͨΊʹఆ਺ΛΠϯλϑΣʔεʹఆٛ͠ ͍ͯΔ public interface PhysicalConstants { double

    AVOGADROS_NUMBER = 6.02214199e23; } public class Atoms implements PhysicalConstants { public double atoms() { return AVOGADROS_NUMBER * mols; } … } NG
  23. ߲໨19 ܕΛఆٛ͢ΔͨΊ͚ͩʹΠϯλϑΣʔεΛ࢖༻ ͢Δ ఆ਺ϢʔςΟϦςΟΫϥε + static Πϯϙʔτ public class PhysicalConstants

    { private PhysicalConstants() {} // ΠϯελϯεԽΛ๷ࢭ public static final double AVOGADROS_NUMBER = 6.02214199e23; } import static net.yanzm.sample.PhysicalConstants.AVOGADROS_NUMBER; public class Atoms { public double atoms() { return AVOGADROS_NUMBER * mols; } … } OK
  24. ߲໨19 ܕΛఆٛ͢ΔͨΊ͚ͩʹΠϯλϑΣʔεΛ࢖༻ ͢Δ Interface ʹఆ਺ΛఆٛͰ͖ͳ͍ interface PhysicalConstants { const val

    AVOGADROS_NUMBER = 6.02214199e23 } object PhysicalConstants { const val AVOGADROS_NUMBER = 6.02214199e23 } ίϯύΠϧΤϥʔ OK τοϓϨϕϧʹఆ਺ΛఆٛͰ͖Δ const val AVOGADROS_NUMBER = 6.02214199e23 ఆ਺ϢʔςΟϦςΟͱͯ͠ object Λ࢖͑͹ෆཁͳΠϯελϯεԽ Λ๷͛Δ OK Kotlin
  25. public class Figure { enum Shape {RECTANGLE, CIRCLE} final Shape

    shape; double length, width; // RECTANGLE ͷ৔߹͚ͩ࢖ΘΕΔ double radius; // CIRLE ͷ৔߹͚ͩ࢖ΘΕΔ Figure(double length, double width) { shape = Shape.RECTANGLE; this.length = length; this.width = width; } Figure(double radius) { shape = Shape.CIRCLE; this.radius = radius; } double area() { switch (shape) { case RECTANGLE: return length * width; case CIRCLE: return Math.PI * radius * radius; default: throw new AssertionError(); } } } NG
  26. abstract class Figure { abstract double area(); } public class

    Rectangle extends Figure { final double length, width; … @Override double area() { return length * width; } } public class Circle extends Figure { final double radius; … @Override double area() { return Math.PI * radius * radius; } } OK
  27. sealed class Λ࢖ͬͨΫϥε֊૚Ͱஔ͖׵͑Δ sealed class Figure { abstract fun area():

    Double } class Rectangle(val length: Double, val width: Double) : Figure() { override fun area(): Double { return length * width } } class Circle(val radius: Double) : Figure() { override fun area(): Double { return Math.PI * radius * radius } } Kotlin
  28. ߲໨21 ઓུΛදݱ͢ΔͨΊʹؔ਺ΦϒδΣΫτΛ࢖༻ ͢Δ public class Person { private final String

    name; private final int age; public Person(String name, int age) { this.name = name; this.age = age; } public static final Comparator<Person> NAME_LENGTH_ORDER = new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.name.length() - o2.name.length(); } }; } OK
  29. data class Person(val name: String, val age: Int) { companion

    object { val NAME_LENGTH_ORDER: Comparator<Person> = Comparator { o1, o2 -> o1.name.length - o2.name.length } } } list.sortedWith(Person.NAME_LENGTH_ORDER) data class Person(val name: String, val age: Int) { companion object { val NAME_LENGTH_ORDER: Comparator<Person> = compareBy { it.name.length } } } ඪ४ؔ਺Λ࢖༻ Kotlin
  30. ߲໨22 ඇ static ͷϝϯόʔΫϥεΑΓ static ͷϝϯ όʔΫϥεΛબͿ ʮΤϯΫϩʔδϯάΠϯελϯε΁ΞΫηε͢Δඞཁ͕ͳ͍ϝϯ όʔΫϥεΛએݴ͢ΔͷͰ͋Ε͹ɺͦͷએݴʹ static

    म০ࢠΛৗ ʹ෇͚Δʯ Java ϝϯόʔΫϥεͷએݴ͸σϑΥϧτ͕ඇ static → ໌ࣔతʹ static Λ෇͚ͳ͍ͱ͍͚ͳ͍ ϝϯόʔΫϥεͷએݴ͸σϑΥϧτ͕ not inner Kotlin
  31. ߲໨23 ݪܕΛ࢖༻͠ͳ͍ List ΍ Map Ͱ͸ͳ͘ɺList<String>, Map<String,String> ͷΑ ͏ʹৗʹܕύϥϝʔλΛࢦఆ͢Δ Ҡߦޓ׵ੑͷͨΊʹݪܕʢList

    ΍ Mapʣ͕࢒͞Ε͍ͯΔ ݪܕ͸࢖༻Ͱ͖ͣɺίϯύΠϧΤϥʔʹͳΔ Java Kotlin List list = new ArrayList<String>(); NG val list :List = ArrayList<String>() ίϯύΠϧΤϥʔ
  32. ߲໨46 ैདྷͷ for ϧʔϓΑΓ for-each ϧʔϓΛબͿ for (int i =

    0; i < a.length; i++) { doSomething(a[i]); } NG OK for (Element e : elements) { doSomething(e); } for-each ͷΈ Kotlin for (e in elements) { doSomething(e) }
  33. Java Ͱ for-each ϧʔϓ͕࢖༻Ͱ͖ͳ͍Α͋͘Δঢ়گ 1. ϑΟϧλϦϯά 2. ม׵ 3. ฒྻΠςϨʔγϣϯ

    Kotlin val even = list.filter { it % 2 == 0 } val square = list.map { it * it } for (i in 0 until min(list1.size, list2.size)) { val a = list1[i] val b = list2[i] }
  34. fun main() { val a = "A" val b =

    "B" val c = a + b val d = "$a, $b" } public static final void main() { String a = "A"; String b = "B"; (new StringBuilder()).append(a).append(b).toString(); (new StringBuilder()).append(a).append(", ").append(b).toString(); } Decompile
  35. ߲໨59 νΣοΫ͞ΕΔྫ֎Λෆඞཁʹ࢖༻͢ΔͷΛආ ͚Δ ʮAPI Λ࢖༻͍ͯ͠ΔϓϩάϥϚ͕͜ΕҎ্ͷ͜ͱ͕Ͱ͖ͳ ͍ͳΒ͹ɺνΣοΫ͞Εͳ͍ྫ֎ͷ΄͏͕ద੾Ͱ͠ΐ͏ɻʯ } catch (TheCheckedException e)

    { throw new AssertionError(); // ܾͯ͠ى͖Δ΂͖Ͱ͸ͳ͍ʂ } } catch (TheCheckedException e) { e.printStackTrace(); // ·͍͍͋΍ɺෛ͚ͩɻ System.exit(1); }
  36. class MainActivity : AppCompatActivity() { private fun versionName(): String {

    return packageManager.getPackageInfo(packageName, 0) .versionName } } public class MainActivity extends AppCompatActivity { private String versionName() { try { return getPackageManager() .getPackageInfo(getPackageName(), 0) .versionName; } catch (PackageManager.NameNotFoundException e) { // ͜͜ʹ͸དྷͳ͍͸ͣ return ""; } } } Java Kotlin
  37. ·ͱΊ • Effective Java ͷଟ͘ͷ߲໨ʹ͍ͭͯɺKotlin Ͱ͸ݴޠ࢓ ༷ͰରԠ͍ͯ͠Δ • Kotlin ʹॻ͖׵͑Δ͜ͱͰɺʮ໌ྎͰɺਖ਼͘͠ɺ࠶ར༻Մ

    ೳͰɺؤڧͰɺॊೈੑ͕͋ΓɺอकՄೳͳϓϩάϥϜΛॻ ͘ʯΤοηϯε͕ࣗવͱऔΓೖΕΒΕΔ
  38. ͓ΘΓ • blog : Y.A.M ͷࡶهா • y-anz-m.blogspot.com • twitter

    : @yanzm ʢ΍Μ͟Ήʣ • uPhyca Inc. (גࣜձࣾ΢ϑΟΧ)