8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

detektのルール日本語訳

Last updated at Posted at 2021-11-19

detektの概要や導入については以下をご参照ください。
Kotlinの静的解析ツール「detekt」のセットアップ方法

注意

公式ページのルールを簡単に翻訳してまとめたものです。
英語に慣れている方は公式ページを直接見るのがいいです。
https://detekt.github.io/detekt/complexity.html

また、間違っている箇所や不適切な箇所がありましたら教えていただけると嬉しいです。

環境

  • Kotlin:1.5.31
  • Android Studio:Arctic Fox
  • detekt:1.18.1

Default

デフォルトで有効になっているルールの一覧です。

Complexity Rule

ComplexCondition

複雑な条件は、どのような場合にその条件が真か偽かを理解するのが難しくなります。複雑な条件を読みやすく理解しやすくするためには、条件を適切な名前の関数や変数に抽出し、それらを代わりに呼び出すことを検討してください。

// bad
val str = "foo"
val isFoo = if (str.startsWith("foo") && !str.endsWith("foo") && !str.endsWith("bar") && !str.endsWith("_")) {
    // ...
}

// good
val str = "foo"
val isFoo = if (str.startsWith("foo") && hasCorrectEnding()) {
    // ...
}

fun hasCorrectEnding() = return !str.endsWith("foo") && !str.endsWith("bar") && !str.endsWith("_")

ComplexMethod

複雑なメソッドは、理解するのも読むのも大変です。複雑なメソッドがどのような副作用を持つのか、明らかではないかもしれません。複雑なメソッドは、より理解しやすい小さなメソッドに分割することをお勧めします。
このルールでは、McCabeのCyclomatic Complexity (MCC)という指標を使用して、関数のソースコード内の線形独立パスの数を測定します。独立したパスの数が多いほど、そのメソッドは複雑です。
MCC循環的複雑度について -Qiita

測定対象
・Conditional statements - if, else if, when
・Jump statements - continue, break
・Loops - for, while, do-while, forEach
・Operators &&, ||, ?:
・Exceptions - catch, use
・Scope Functions - let, run, with, apply, and also

LargeClass

このルールでは、大規模なクラスを報告します。クラスは通常、1つの責任を持つべきです。一度に多くのことをするのではなく、大きなクラスを小さなクラスに分割することをお勧めします。
デフォルトはクラス内のソースコードの行数は600以内にするのがベスト

LongMethod

メソッドは1つの責任を持つべきです。行数長いメソッドは、そのメソッドが一度に多くのケースを処理していることを示します。機能を明確に説明して分かりやすい名前付きの行数少なくて小さなメソッドをお勧めします。
行数長いメソッドの機能の一部を別の小さなメソッドに抽出します。
デフォルトはメソッド内のソースコードの行数は60以内にするのがベスト

LongParameterList

コンストラクタや関数の引数の数は少なくすべきです。

// bad
fun sign(userName: String, userId: Int, userSex: String, userMail: String, isCheck: Boolean, token: String) { }

// good
data class User (
  var userName: String = "", 
  var userId: Int = 0, 
  var userSex: String = "",
  var userMail: String = "",
)
fun sign(user: User, isCheck: Boolean, token: String) { }

NestedBlockDepth

最大4レベルの深さでネストすべきです。

// bad
fun f_0(type: ItemType, isCheck: Boolean) {
   when (type) {
      ItemType.Article -> {
          if (isCheck) {
              if (article.isNotLoading) {
                    //do something
              }
          }
      }
   }
}
// good
fun f_0(type: ItemType, isCheck: Boolean) {
   when (type) {
      ItemType.Article -> {
          f_1(isCheck)
      }
   }
}
fun f_1(isCheck: Boolean) {
     if (isCheck) {
         if (article.isNotLoading) {
               //do something
          }
     }
}
TooManyFunctions

ファイル、クラス、インターフェース、オブジェクト、および列挙内の関数を少なくすべき。
関数が多すぎると、単一責任原則の違反となります。明らかにコードの別の部分にまとめられている機能を抽出することをお勧めします。
デフォルト11個以内

Empty-blocks

EmptyCatchBlock

空のキャッチブロックがあります。
空のキャッチ・ブロックは、例外が無視され、処理されないことを示します。例外を意図的に無視する場合は、 allowedExceptionNameRegexに指定した名前を使用して明示する必要があります。

// bad
fun f() {
     try {
        //do something
     } catch (e: PackageManager.NameNotFoundException) {

     }
}
// good
fun f() {
     try {
        //do something
     } catch (e: PackageManager.NameNotFoundException) {
        //do something for fixing Exception
     }
}
EmptyClassBlock

空のクラスがあります。

EmptyDefaultConstructor

空のデフォルトコンストラクタがあります。

EmptyDoWhileBlock

空のdo/whileループがあります。

EmptyElseBlock

空のelseブロックがあります。

EmptyFinallyBlock

空のfinallyブロックがあります。

EmptyForBlock

空のforブロックがあります。

EmptyFunctionBlock

空の関数ブロックがあります。

EmptyIfBlock

空のifブロックがあります。

EmptyInitBlock

空のinitブロックがあります。

EmptyKtFile

空のKotlinファイルがあります。

EmptySecondaryConstructor

空のセカンダリコンストラクタブロックがあります。

EmptyTryBlock

空のtryブロックがあります。

EmptyWhenBlock

空のwhenブロックがあります。

EmptyWhileBlock

空のwhileブロックがあります。

Exceptions

ExceptionRaisedInUnexpectedLocation

絶対に例外を発生させてはいけない関数内で例外をスローしてはいけない

// bad
class Foo {
    override fun toString(): String {
        throw IllegalStateException() // exception should not be thrown here
    }
}
// good
class Foo {
    override fun toString(): String {
       //do something
    }
}
PrintStackTrace

例外を握りつぶしてはいけない

// bad
fun foo() {
    Thread.dumpStack()
}
fun bar() {
    try {
        // ...
    } catch (e: IOException) {
        e.printStackTrace()
    }
}
// good
val LOGGER = Logger.getLogger()
fun bar() {
    try {
        // ...
    } catch (e: IOException) {
        LOGGER.info(e)
    }
}
RethrowCaughtException

例外をキャッチされた後、修正なしで再スローしてはいけない

// bad
fun foo() {
    try {
        // ...
    } catch (e: IOException) {
        throw e
    }
}
// good
fun foo() {
    try {
        // ...
    } catch (e: IOException) {
        throw MyException(e)
    }
    try {
        // ...
    } catch (e: IOException) {
        print(e)
        throw e
    }
    try {
        // ...
    } catch (e: IOException) {
        print(e.message)
        throw e
    }
}
ReturnFromFinally

finallyブロックでreturn文を使用すると、tryブロックでスローされた例外を破棄されるので
finallyブロックでreturn文を使用してはいけない

// bad
fun foo() {
    try {
        throw MyException()
    } finally {
        return // prevents MyException from being propagated
    }
}

// good
fun foo() {
    try {
        throw MyException()
    } catch(e: MyException) {
        // do something
    }
}
SwallowedException

例外を使用しないといけない。または例外をスローすべき

// bad
fun foo() {
    try {
        // ...
    } catch(e: IOException) {
        throw MyException(e.message) // e is swallowed
    }
    try {
        // ...
    } catch(e: IOException) {
        throw MyException() // e is swallowed
    }
    try {
        // ...
    } catch(e: IOException) {
        bar() // exception is unused
    }
}

// good
fun foo() {
    try {
        // ...
    } catch(e: IOException) {
        throw MyException(e)
    }
    try {
        // ...
    } catch(e: IOException) {
        println(e) // logging is ok here
    }
}
ThrowingExceptionFromFinally

finallyブロックから例外を投げることは、混乱や例外の破棄につながるため、避けるべきです。

// bad
fun foo() {
    try {
        // ...
    } finally {
        throw IOException()
    }
}
// good
fun foo() {
    try {
        // ...
    } catch (e: MyException) {
        throw IOException()
    }
}
ThrowingExceptionsWithoutMessageOrCause

対象例外は引数や詳細な説明なしにスローされてはいけない。

デフォルト対象
['ArrayIndexOutOfBoundsException', 'Exception', 'IllegalArgumentException', 'IllegalMonitorStateException', 'IllegalStateException', 'IndexOutOfBoundsException', 'NullPointerException', 'RuntimeException', 'Throwable']

// bad
fun foo(bar: Int) {
    if (bar < 1) {
        throw IllegalArgumentException()
    }
    // ...
}
// good
fun foo(bar: Int) {
    if (bar < 1) {
        throw IllegalArgumentException("bar must be greater than zero")
    }
    // ...
}
ThrowingNewInstanceOfSameException

例外を同じ例外タイプの中に包んで、再度スローすることはしてはいけない。

// bad
fun foo() {
    try {
        // ...
    } catch (e: IllegalStateException) {
        throw IllegalStateException(e) // rethrows the same exception
    }
}
// good
fun foo() {
    try {
        // ...
    } catch (e: IllegalStateException) {
        throw MyException(e)
    }
}
TooGenericExceptionCaught

現在処理されているケースに対する特定の例外をキャッチすることが望ましい
キャッチする例外の範囲が広すぎると、意図しない例外がキャッチされてしまう可能性があります

デフォルト対象
['ArrayIndexOutOfBoundsException', 'Error', 'Exception', 'IllegalMonitorStateException', 'IndexOutOfBoundsException', 'NullPointerException', 'RuntimeException', 'Throwable']

// bad
fun foo() {
    try {
        // ... do some I/O
    } catch(e: Exception) { } // too generic exception caught here
}
// good
fun foo() {
    try {
        // ... do some I/O
    } catch(e: IOException) { }
}
TooGenericExceptionThrown

現在発生しているケースに対して特定の例外をスローすることが望ましい
スローする例外の範囲が広すぎると、意図しない例外がキャッチされてしまう可能性があります

デフォルト対象
['Error', 'Exception', 'RuntimeException', 'Throwable']

// bad
fun foo(bar: Int) {
    if (bar < 1) {
        throw Exception() // too generic exception thrown here
    }
    // ...
}

// good
fun foo(bar: Int) {
    if (bar < 1) {
        throw IllegalArgumentException("bar must be greater than zero")
    }
    // ...
}

Naming

ClassNaming

指定された命名規則に従わないクラスやオブジェクトがあります
(default: '[A-Z][a-zA-Z0-9]*')

ConstructorParameterNaming

指定された命名規則に従わないコンストラクタのパラメータがあります
(default: '[a-z][A-Za-z0-9]*')

EnumNaming

指定された命名規則に従わないEnumがあります
(default: '[A-Z][_a-zA-Z0-9]*')

FunctionNaming

指定された命名規則に従わない関数があります
(default: '([a-z][a-zA-Z0-9]*)|(.*)')

FunctionParameterNaming

指定された命名規則に従わない関数のパラメータがあります
(default: '[a-z][A-Za-z0-9]*')

MatchingDeclarationName

ファイルの名前は、そのファイルの中のコードが何をするかを説明するものでなければなりません。

// bad
class Foo // FooUtils.kt

fun Bar.toFoo(): Foo = ...
fun Foo.toBar(): Bar = ...

// good
class Foo { // Foo.kt
    fun stuff() = 42
}

fun Bar.toFoo(): Foo = ...

MemberNameEqualsClassName

クラスまたはオブジェクトと同じ名を持つメンバー関数や変数があります。
クラスのインスタンスを作成するファクトリー関数は、このルールの対象外です。

// bad
class MethodNameEqualsClassName {

    fun methodNameEqualsClassName() { }
}

class PropertyNameEqualsClassName {

    val propertyEqualsClassName = 0
}

// good
class Manager {

    companion object {
        // factory functions can have the same name as the class
        fun manager(): Manager {
            return Manager()
        }
    }
}

ObjectPropertyNaming

指定された命名規則に従わないオブジェクト内のプロパティがあります。

constantPattern (default: '[A-Za-z][A-Za-z0-9]')
propertyPattern (default: '[A-Za-z][_A-Za-z0-9]
')
private PropertyPattern (default: '(
)?[A-Za-z][_A-Za-z0-9]*')

PackageNaming

指定された命名規則に従わないパッケージがあります。

(default: '[a-z]+(.[a-z][A-Za-z0-9])')

TopLevelPropertyNaming

指定された命名規則に従わないトップレベルの定数があります。

constantPattern (default: '[A-Z][A-Z0-9]')
propertyPattern (default: '[A-Za-z][_A-Za-z0-9]
')
private PropertyPattern (default: '
?[A-Za-z][_A-Za-z0-9]*')

VariableNaming

指定された命名規則に従わない変数があります。

variablePattern (default: '[a-z][A-Za-z0-9]')
private VariablePattern (default: '(_)?[a-z][A-Za-z0-9]
')

Performance

ArrayPrimitive

Arrayを使用すると、暗黙のボックス化が発生し、パフォーマンスが低下します。Kotlin に特化した Array インスタンスの使用を推奨します。

// bad
fun function(array: Array<Int>) { }

fun returningFunction(): Array<Double> { }

// good
fun function(array: IntArray) { }

fun returningFunction(): DoubleArray { }
ForEachOnRange

範囲内でforEachメソッドを使用すると、パフォーマンスが大きく低下します。シンプルなforループの使用をお勧めします。
ベンチマークによると、レンジに対してforEachを使用すると、単純なforループに比べてパフォーマンスが大幅に低下することがわかっています。

// bad
(1..10).forEach {
    println(it)
}
(1 until 10).forEach {
    println(it)
}
(10 downTo 1).forEach {
    println(it)
}

// good
for (i in 1..10) {
    println(i)
}
SpreadOperator

ほとんどの場合、拡散演算子を使用すると、メソッドを呼び出す前に、配列の完全なコピーが作成されます。
v1.1.60 以降の Kotlin コンパイラには、配列コンストラクタ関数が vararg パラメータに渡される引数の作成に使用されている場合、配列のコピーをスキップする最適化が施されています。

// bad
val strs = arrayOf("value one", "value two")
val foo = bar(*strs)

fun bar(vararg strs: String) {
    strs.forEach { println(it) }
}

// good
// array copy skipped in this case since Kotlin 1.1.60
val foo = bar(*arrayOf("value one", "value two"))

// array not passed so no array copy is required
val foo2 = bar("value one", "value two")

fun bar(vararg strs: String) {
    strs.forEach { println(it) }
}
UnnecessaryTemporaryInstantiation

プリミティブ型を String に変換する際、一時的なオブジェクトを使用しないようにしました。これは、プリミティブ型を直接使用する場合と比較して、パフォーマンス上のペナルティがあります。

// bad
val i = Integer(1).toString() // temporary Integer instantiation just for the conversion

// good
val i = Integer.toString(1)

Potential-bugs

DuplicateCaseInWhenExpression

when式の中に重複したcase文フラグがあります。

// bad
when (i) {
    1 -> println("one")
    1 -> println("one")
    else -> println("else")
}

// good
when (i) {
    1 -> println("one")
    else -> println("else")
}
EqualsAlwaysReturnsTrueOrFalse

常にtrueまたはfalseを返すequals()メソッドがあります。

// bad
override fun equals(other: Any?): Boolean {
    return true
}

// good
override fun equals(other: Any?): Boolean {
    return this === other
}
EqualsWithHashCodeExist

クラスがequals()メソッドをオーバーライドする際には、hashCode()メソッドもオーバーライドする必要があります。

// bad
class Foo {

    override fun equals(other: Any?): Boolean {
        return super.equals(other)
    }
}

// good
class Foo {

    override fun equals(other: Any?): Boolean {
        return super.equals(other)
    }

    override fun hashCode(): Int {
        return super.hashCode()
    }
}
ExplicitGarbageCollectionCall

ガベージコレクタを明示的に呼び出す必要はありません。

ImplicitDefaultLocale

文字列のフォーマットや大文字小文字の変換を行う際に、暗黙のデフォルト値を使用するよりも、[java.util.Locale]を明示的に渡すことをお勧めします。
デフォルトのロケールは、ほとんどの場合、HTTPヘッダのような機械で読めるテキストには不適切です。

// bad
String.format("Timestamp: %d", System.currentTimeMillis())

val str: String = getString()
str.toUpperCase()
str.toLowerCase()

// good
String.format(Locale.US, "Timestamp: %d", System.currentTimeMillis())

val str: String = getString()
str.toUpperCase(Locale.US)
str.toLowerCase(Locale.US)
InvalidRange

無効な範囲があります。

// bad
for (i in 2..1) {}
for (i in 1 downTo 2) {}

val range1 = 2 until 1
val range2 = 2 until 2

// good
for (i in 2..2) {}
for (i in 2 downTo 2) {}

val range = 2 until 3
IteratorHasNextCallsNextMethod

IteratorインターフェースのhasNext()メソッド内でnextメソッドを呼び出してはいけない。

// bad
class MyIterator : Iterator<String> {

    override fun hasNext(): Boolean {
        return next() != null
    }
}

// good
class MyIterator : Iterator<String> {

    override fun hasNext(): Boolean {
        return // ...
    }

  override fun next(): String {
        return // ...
    }
}
IteratorNotThrowingNoSuchElementException

next()メソッドの実装で返すべき要素がなくなったときに NoSuchElementException を投げるべきです。

// bad
class MyIterator : Iterator<String> {

    override fun next(): String {
        return ""
    }
}

// good
class MyIterator : Iterator<String> {

    override fun next(): String {
        if (!this.hasNext()) {
            throw NoSuchElementException()
        }
        // ...
    }
}
RedundantElseInWhen

冗長なelseケースを含むwhen式があります。

// bad
enum class Color {
    RED,
    GREEN,
    BLUE
}

fun whenOnEnumFail(c: Color) {
    when(c) {
        Color.BLUE -> {}
        Color.GREEN -> {}
        Color.RED -> {}
        else -> {} // no use
    }
}

// good
enum class Color {
    RED,
    GREEN,
    BLUE
}

fun whenOnEnumCompliant(c: Color) {
    when(c) {
        Color.BLUE -> {}
        Color.GREEN -> {}
        else -> {}
    }
}

fun whenOnEnumCompliant2(c: Color) {
    when(c) {
        Color.BLUE -> {}
        Color.GREEN -> {}
        Color.RED -> {}
    }
}
UnnecessaryNotNullOperator

不要なnot-null演算子(!!)の使用があります。

// bad
val a = 1
val b = a!!

// good
val a = 1
val b = a
UnnecessarySafeCall

不要なセーフコール演算子(?)使用があります。

// bad
val a: String = ""
val b = a?.length

// good
val a: String? = null
val b = a?.length
UnreachableCode

呼び出されないコードがあります。

// bad
for (i in 1..2) {
    break
    println() // unreachable
}

throw IllegalArgumentException()
println() // unreachable

fun f() {
    return
    println() // unreachable
}


// good
for (i in 1..2) {
  println()
    break
}

println()
throw IllegalArgumentException()

fun f() {
   println()
    return
}

UnsafeCallOnNullableType

nullable型に対する安全でない呼び出しがあります。

// bad
fun foo(str: String?) {
    println(str!!.length)
}

// good
fun foo(str: String?) {
    println(str?.length)
}

UnsafeCast

絶対に成功しないキャストがあります。

// bad
fun foo(s: String) {
    println(s as Int)
}

fun bar(s: String) {
    println(s as? Int)
}

// good
fun foo(s: Any) {
    println(s as Int)
}
WrongEqualsTypeParameter

誤って型付けされたパラメータを取り込む equals() メソッドがあります。

// bad
class Foo {

    fun equals(other: String): Boolean {
        return super.equals(other)
    }
}

// good
class Foo {

    fun equals(other: Any?): Boolean {
        return super.equals(other)
    }
}

Style

EqualsNullCall

オブジェクトをnullと比較するには、==を使用することをお勧めします。

// bad
fun isNull(str: String) = str.equals(null)

// good
fun isNull(str: String) = str == null
ForbiddenComment

開発時にのみ使用されるべきコメントがあります。

// bad
val a = "" // TODO: remove please
// FIXME: this is a hack
fun foo() { }
// STOPSHIP:
ForbiddenPublicDataClass

データクラスは、パブリックAPIのバイナリ互換性に悪影響を及ぼします。使用を避けてください。

このルールはライブラリのメンテナに向けたものです。もしあなたが最終的なアプリケーションを開発しているのであれば、この問題は無視して構いません。

// bad
data class C(val a: String) // violation: public data class

// good
internal data class C(val a: String)
FunctionOnlyReturningConstant

1つの定数を返すだけの関数があります。const valを使用すべき。

// bad
fun functionReturningConstantString() = "1"

// good
const val constantString = "1"
LibraryCodeMustSpecifyReturnType

ライブラリのパブリックAPIとして公開される関数やプロパティには、明示的な戻り値の型が必要です。

// bad
// code from a library
val strs = listOf("foo, bar")
fun bar() = 5
class Parser {
    fun parse() = ...
}

// good
// code from a library
val strs: List<String> = listOf("foo, bar")
fun bar(): Int = 5

class Parser {
    fun parse(): ParsingResult = ...
}
LibraryEntitiesShouldNotBePublic

ライブラリのタイプエイリアスとクラスは、internalまたはprivateを使用すべき

// bad
// code from a library
class A

// good
// code from a library
internal class A
LoopWithTooManyJumpStatements

複数のbreakまたはcontinueステートメントを含むループは、読んで理解しにくい。

// bad
val strs = listOf("foo, bar")
for (str in strs) {
    if (str == "bar") {
        break
    } else {
        continue
    }
}
MagicNumber

マジックナンバーの使用があります。

// bad
class User {

    fun checkName(name: String) {
        if (name.length > 42) {
            throw IllegalArgumentException("username is too long")
        }
        // ...
    }
}

// good
class User {

    fun checkName(name: String) {
        if (name.length > MAX_USERNAME_SIZE) {
            throw IllegalArgumentException("username is too long")
        }
        // ...
    }

    companion object {
        private const val MAX_USERNAME_SIZE = 42
    }
}
MaxLineLength

定義された最大行長を超えるコードがあります。

(default: 120行)

MayBeConst

const val を使用する可能性のあるプロパティ (val) があります。
const valを使用すると、生成されるバイトコードのパフォーマンスが向上するだけでなく、Javaとの相互運用性も向上します。

// bad
val myConstant = "abc"

// good
const val MY_CONSTANT = "abc"
}
ModifierOrder

正しい順序でない修飾語があります。

// bad
lateinit internal val str: String

// good
internal lateinit val str: String
}
NestedClassesVisibility

ネストクラスが親クラスよりアクセスレベル高い修飾語の使用があります。

// bad
internal class Outer {
    // explicit public modifier still results in an internal nested class
    public class Nested
}


// good
internal class Outer {
    class Nested1
    internal class Nested2
}
NewLineAtEndOfFile

ファイル末尾に改行を入れるべき

実際に「C, C++」では、改行なしのソースファイルだと未定義扱いになり誤作動を引き起こす、とのこと。

OptionalAbstractKeyword

不要で削除可能な抽象的な修飾語があります。

// bad
abstract interface Foo { // abstract keyword not needed

    abstract fun x() // abstract keyword not needed
    abstract var y: Int // abstract keyword not needed
}

// good
interface Foo {

    fun x()
    var y: Int
}
ProtectedMemberInFinalClass

Kotlin のクラスはデフォルトで final です。したがって、open と protected メンバを含むべきではありません。代わりに private や internal を使うべき

// bad
class ProtectedMemberInFinalClass {
    protected var i = 0
}

// good
class ProtectedMemberInFinalClass {
    private var i = 0
}
ReturnCount

メソッド内で使用できるreturnの数を制限する

(default: 2)

// bad
fun foo(i: Int): String {
    when (i) {
        1 -> return "one"
        2 -> return "two"
        else -> return "other"
    }
}

// good
fun foo(i: Int): String {
    return when (i) {
        1 -> "one"
        2 -> "two"
        else -> "other"
    }
}
SafeCast

このルールはキャストを検査し、代わりに安全なキャストに置き換えることができる

// bad
fun numberMagic(number: Number) {
    val i = if (number is Int) number else null
    // ...
}


// good
fun numberMagic(number: Number) {
    val i = number as? Int
    // ...
}
SerialVersionUIDInSerializableClass

Serializableインターフェースを実装しているクラスは、serialVersionUIDも正しく宣言する必要があります

// bad
class IncorrectSerializable : Serializable {

    companion object {
        val serialVersionUID = 1 // wrong declaration for UID
    }
}

// good
class CorrectSerializable : Serializable {

    companion object {
        const val serialVersionUID = 1L
    }
}
ThrowsCount

メソッド内で使用できるthrowの数を制限する

(default: 2)

// bad
fun foo(i: Int) {
    when (i) {
        1 -> throw IllegalArgumentException()
        2 -> throw IllegalArgumentException()
        3 -> throw IllegalArgumentException()
    }
}

// good
fun foo(i: Int) {
    when (i) {
        1,2,3 -> throw IllegalArgumentException()
    }
}
UnnecessaryAbstractClass

抽象クラスが具象メンバーを持たない場合は、インターフェイスにリファクタリングする必要があります。
抽象的なメンバーを定義していない抽象クラスは、代わりに具象クラスにリファクタリングする必要があります。

// bad
abstract class OnlyAbstractMembersInAbstractClass { // violation: no concrete members

    abstract val i: Int
    abstract fun f()
}

abstract class OnlyConcreteMembersInAbstractClass { // violation: no abstract members

    val i: Int = 0
    fun f() { }
}
UnnecessaryApply

apply式は頻繁に使用されますが、見た目の複雑さを軽減するために、通常のメソッドや拡張機能の呼び出しに置き換えるべき。

// bad
config.apply { version = "1.2" } // can be replaced with `config.version = "1.2"`
config?.apply { environment = "test" } // can be replaced with `config?.environment = "test"`
config?.apply { println(version) } // `apply` can be replaced by `let`

// good
config.apply {
    version = "1.2"
    environment = "test"
}
UnnecessaryInheritance

不要なスーパータイプあります。
AnyやObjectからの継承は不要

// bad
class A : Any()
class B : Object()

// good
class A
class B
UnusedPrivateClass

使用されていないプライベートクラスがあります。

UnusedPrivateMember

使用されていないプライベートプロパティ、関数パラメータがあります。

UselessCallOnNotNull

Kotlin stdlibには、NULLの可能性がある参照を操作するために設計された関数がいくつか用意されています。

// bad
val testList = listOf("string").orEmpty()
val testList2 = listOf("string").orEmpty().map { _ }
val testList3 = listOfNotNull("string")
val testString = ""?.isNullOrBlank()

// good
val testList = listOf("string")
val testList2 = listOf("string").map { }
val testList3 = listOf("string")
val testString = ""?.isBlank()
UtilityClassWithPublicConstructor

具体的な実装を伴わないユーティリティー変数や関数のみを含むクラスは、オブジェクトや非公開のコンストラクタを持つクラスにリファクタリングすることができます。

// bad
class UtilityClassViolation {

    // public constructor here
    constructor() {
        // ...
    }

    companion object {
        val i = 0
    }
}

open class UtilityClassViolation private constructor() {

    // ...
}

// good
class UtilityClass {

    private constructor() {
        // ...
    }

    companion object {
        val i = 0
    }
}
object UtilityClass {

    val i = 0
}
VarCouldBeVal

var宣言は、再割り当てされないため、valにすべき

// bad
fun example() {
    var i = 1 // violation: this variable is never re-assigned
    val j = i + 1
}

// good
fun example() {
    val i = 1
    val j = i + 1
}
WildcardImport

ワイルドカードによるインポートは、完全修飾クラス名を用いたインポートに置き換えるべきです。これにより、どのクラスがインポートされているかが明確になり、ネーミングコンフリクトを防ぐことができます。

// bad
import io.gitlab.arturbosch.detekt.*

class DetektElements {
    val element1 = DetektElement1()
    val element2 = DetektElement2()
}

// good
import io.gitlab.arturbosch.detekt.DetektElement1
import io.gitlab.arturbosch.detekt.DetektElement2

class DetektElements {
    val element1 = DetektElement1()
    val element2 = DetektElement2()
}
8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?