Java like "assert" statement


#1

Both Java and Kotlin have “assert” statement, they look equals but are greatly different.

Java assert is per class enabled and expression evaluation is done only when assertion is enabled.

By default, assertions are disabled, so you must not assume that the Boolean expression contained in an assertion will be evaluated. Therefore, your expressions must be free of side effects.

http://www.oracle.com/us/technologies/java/assertions-139853.html

Differently, Kotlin assertion is a simple function, therefore assertion cannot be per class enabled and evaluation is done always.

Why compute unuseful checks in production?

Java “assert” is really more powerful then Kotlin version.
Do you consider to fix this gap?


#2

The piece of documentation you quote is a great explanation for why the design of the Java assert is problematic: putting code with required side effects into an assert statement is such a common bug source in both C and Java.

And I would claim that the behavior of any sufficiently complex production system can’t be understood well enough to state that assert checks are unuseful.


#3

A related issue: https://youtrack.jetbrains.com/issue/KT-7540


#4

putting code with required side effects into an assert statement is such a common bug source in both C and Java.

My experience is different: I use assert to early detect bugs and those work well.
I found this issue after a fine tuning session, unfortunately now I must comment all Kotlin assert in my code; I don’t see this as a benefit, really.

Do you consider this a bad practice?

Do you evaluate this check in a loop and you discard the result only in production?


#5

To preserve both assertion styles, is it possible to evaluate to insert a different function?

Something like:

@kotlin.internal.InlineOnly
public inline fun invariant(check: () -> Boolean) {
    if (_Assertions.ENABLED && !check()) {
        throw AssertionError()
    }
}

#6

IntelliJ IDEA and its plugins always run with the -ea option enabled. Assertion failures are reported to JetBrains via the exception reporter, and are an extremely useful tool for us to improve the stability of our products.


#7

There may be always useful, but sometimes they’re more expensive than useful. In Java, I mostly use Guava checkArgument or verify (both doing an unconditional check) and only sometimes assert. I recall having used a lot of asserts when doing some numerical computation as there were many possible very time-consuming checks.


#8

In places where you need optional asserts, you can use something like this (untested):

val assertionsEnabled = Thread.currentThread().javaClass.desiredAssertionStatus()

inline fun assert(message: String = "Assertion failure", test: () -> Boolean) {
    if (assertionsEnabled && !test()) throw AssertionError(message)
}

I believe this will compile to bytecode similar to what javac produces, and that the JIT compilers are very good at eliminating if the -ea flag is not passed.


#9

Signatures

fun assert(message: String = "Assertion failed", lazyTest: () -> Boolean)

and

fun assert(test: Boolean, lazyMessage: () -> Any = { "Assertion failed" })

may confuse with each other.

Different names point to different motivation (and make code more understandable).


Moreover:

Thread.currentThread().javaClass.desiredAssertionStatus()

is equal to

Thread::java.class.desiredAssertionStatus()

#10

Yeah, I know there’s an assert function already defined. Anyone who wants to use that should come up with a different name that isn’t already taken.

Good point about being able to write it shorter.


#11

I have come up with a cool solution to get java like asserts that can be disabled in the class and it only requires adding one line of boilerplate in the companion object for the class and lets you decide what you want the assertion mechanism to be called.

The problem is that it won’t work until 1.1.1 due to this bug: https://youtrack.jetbrains.com/issue/KT-16441

Here is the code: to support it. FYI it uses kotlin.reflect

interface AssertionsEnabled

inline operator fun AssertionsEnabled?.invoke(assertion: () -> Boolean)
{
    this?.run { assert(assertion()) }
}

inline operator fun AssertionsEnabled?.invoke(message:String, assertion: () -> Boolean)
{
    this?.run { assert(assertion()) {message} }
}

object DisablableAssert : ReadOnlyProperty<Any, AssertionsEnabled?>
{
    object enabled : AssertionsEnabled

    operator fun provideDelegate(thisRef: Any, prop: KProperty<*>): ReadOnlyProperty<Any, AssertionsEnabled?> = this

    override fun getValue(thisRef: Any, property: KProperty<*>): AssertionsEnabled?
    {
        return thisRef::class
                .apply { if(!isCompanion) throw IllegalStateException("only allowed for companion objects") }
                .run { qualifiedName!!.removeSuffix(".$simpleName") }
                .run { Class.forName(this) }
                .run { if(desiredAssertionStatus()) enabled else null }
    }
}

And here is how it is used. Whatever name you give to the property is how you access it so you can call it assert, invariant, or whatever you want. But as I said this won’t work until Kotlin 1.1.1

class MyClass
{
    companion object
    {
        val myAssert : AssertionsEnabled? by DisablableAssert
    }

    fun foo()
    {
        myAssert { 1 == 1 }
        myAssert("this will only be checked if assertions enabled") { 1 == 2 }
    }
}

#12

Oh and add a version with a function to generate the message:

inline operator fun AssertionsEnabled?.invoke(message:() -> String, assertion: () -> Boolean)
{
    this?.run { assert(assertion(), message) }
}