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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →
Avatar for Yuki Anzai Yuki Anzai
August 25, 2018
14k

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

Avatar for Yuki Anzai

Yuki Anzai

August 25, 2018

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. (גࣜձࣾ΢ϑΟΧ)