At present, I have a custom error defined like so:
class IsEvenError(message:String):Exception(message)
val n = 10;
if (n%2 == 0) {
throw IsEvenError("${n} is even");
}
The problem with this is, I have to manually write out the error message every time I want to throw it.
I want to be able to embed the error message into the class itself, so I can do something like:
throw IsEvenError(n); // this should throw an error saying "10 is even".
How do I accomplish this?
You can change your IsEvenError to accept the number instead of a string, and pass the formatted string to Exception:
class IsEvenError(number: Int) : Exception("$number is even")
fun main() : Unit = throw IsEvenError(10)
Produces:
Exception in thread "main" IsEvenError: 10 is even
at TestKt.main(Test.kt:2)
Related
I have a kotlin class with a method
loadElements(e: Iterable<Int>) {
}
This then constructs a new copy of that Iterable as an ArrayList<Int> within the object.
It is a requirement that all the elements in that ArrayList<Int> be non-negative. It is considered a breach of contract by the caller if that is not met. I've been led to believe that "breach of contract" is something to be tested by require(), whereas check() is for testing logic internal to that method. Is this correct ?
All the examples I have seen, have the require() as the very first lines of code within the method. Is it, however, acceptable to run require() in a loop, like this ?
public fun loadElements(e: Iterable<Int>) {
elementArray.clear()
e.forEach {
require(it>=0)
elementArray.add(it)
moduleCount += it
}
if (elementCount %2 == 1)
elementArray.add(0)
check(elementCount %2 == 0)
computeInternalSizes()
}
Thing is, this means that part of the object's internals may already be set-up by the time the require() breach is detected: i.e., moduleCount will be wrong and computeInternalSizes() will never get called.
Now, of course I could just use a separate pass, with the first one checking for the require() condition, and then doing all the real work thereafter. This would mean that if the input came in as a Sequence<Int>, it would be forced to be terminal and multi-iterable.
If the require() throws, I would like to assume that the program cannot continue because a design error has occurred somewhere. However, if someone traps the resultant exception, and continues, I will end-up with an incoherent object state.
What is best practice for handling conditions where incoming parameter breaches won't be noticed until some significant unrewindable work has been done ?
I tried using a separate pass for checking for non-negativity. This worked perfectly well but, given that it could be coming from a Sequence or similar, I don't want to have to build the whole sequence, and then traverse that sequence again.
I tried using check(). This works, but it just shows up as an inconsistency in object state, rather than flagging up the incoming parameter validation, which is making a breach of contract look like an internal design fault, and just delaying the inevitable.
I've tried putting try/catch/finally all over the place, but this is an excessive amount of code for such a simple thing.
I'm not even sure if a program should attempt recovery if a require() fails.
In general you avoid situations like this, by reducing the scope of mutability in your code.
The difference between require and check is mostly a convention. They throw different Exceptions, namely IllegalArgumentException and IllegalStateException respectively. As the type of the Exceptions suggest, former is suited for validating the (user) input to a method whereas the latter is designed to check intermediate states during the runtime.
Exceptions in Kotlin should be handled as such, being an Exception that should not occur regularly. See also the Kotlin documentation why there are no checked exceptions in Kotlin.
You did not write the name of your surrounding Kotlin class, thus I'll call it Foo for the time being.
Rather than providing a function on Foo, that mutates the internal state of Foo, you could create new instances of Foo based on the Iterable<Int> / Sequence<Int>. This way, you only ever have an Foo object when its in a valid state.
private class Foo(source: Iterable<Int>) {
private val elementArray = ArrayList<Int>()
private val moduleCount: Int
init {
var internalCount = 0
for (count in source) {
require(count > 0)
elementArray.add(count)
internalCount += count
}
moduleCount = internalCount
if (elementArray.size % 2 == 1) {
elementArray.add(0)
}
check(elementArray.size % 2 == 0)
// ...
}
}
Alternatively, if you want / need to keep the interface as described in your question but also avoid the invalid state, you could make use of an intermediate copy.
As you're copying the incoming Iterable<Int> / Sequence<Int> into an ArrayList<Int> I assume you're not working with very large collections.
private class Foo(source: Iterable<Int>) {
private val elementArray = ArrayList<Int>()
private var moduleCount = 0
public fun loadElements(source: Iterable<Int>) {
val internalCopy = ArrayList<Int>()
for (count in source) {
require(count >= 0)
internalCopy.add(count)
}
elementArray.clear()
for (count in internalCopy) {
elementArray.add(count)
moduleCount += count
}
if (elementArray.size % 2 == 1) {
elementArray.add(0)
}
check(elementArray.size % 2 == 0)
// ...
}
}
I'm just learning Mutiny and I need to implement retry logic.
I have this code:
fun main() {
getResult()
.onFailure().invoke { t -> println("Got error: $t") }
.onFailure().retry().atMost(2)
.subscribe().with(
{ result -> println(result) },
{ t -> t.printStackTrace() }
)
}
fun getResult(): Uni<String?> {
println("Preparing result...")
return Uni.createFrom().failure(Exception("Some error happened"))
}
So, the getResult() is a function that may misbehave and needs to be called multiple times on failure.
When I run this program, this is what's happening:
Preparing result...
Got error: java.lang.Exception: Some error happened
Got error: java.lang.Exception: Some error happened
Got error: java.lang.Exception: Some error happened
java.lang.Exception: Some error happened
at MainKt.getResult(Main.kt:16)
at MainKt.main(Main.kt:4)
Obiously, the getResult() function is called only once, while the onFailure() stages actually executed three times.
Is there anything that Mutiny could help me to execute getResult() function on each failure? I sure can implement this with a simple loop, but I feel like Mutiny should already have something like this.
Unfortunately, I didn't find anything suitable in the docs.
Your Uni in getResult is created with an "immediate" item, which is cached and never computed again.
Use Uni.createFrom().failure(() -> Exception("Some error happened"))
In this case, it's a supplier, so it won't be cached but called on every attempt.
So, the right solution for this is actually using the Uni.deferred() method like this:
fun main() {
Uni.createFrom().deferred { getResult() }
.onFailure().invoke { t -> println("Got error: $t") }
.onFailure().retry().atMost(2)
.subscribe().with(
{ result -> println(result) },
{ t -> t.printStackTrace() }
)
}
Thanks to Boris the Spider, who suggested to use the deferred(), and to Clement, who clarified its use with null values.
Initially, I misinterpreted the deferred() documentation thinking it's not allowed to return a null value, but actually it's OK for a Supplier to return a Uni of null:
Uni.createFrom.deferred { Uni.createFrom().nullItem() }
What the docs really are prohibiting is returning a null instead of a Uni:
Uni.createFrom().deferred { null }
class Solution {
val message: String //error : val must be initialized or abstract
message = "love" //error : val cannot be reassigned
}
I understand what's happening in here - val cannot be reassigned.
So when I need val but can not initialize it i used to use by lazy
class Solution {
fun love(){
val message : String
message = "love" //this works
message = "hate" //this is error "val cannot be reassigned"
}
}
Here I can delcare val without initialization and later write codemessage = "love".what's happening here?
#deHaar noticed correctly that only var (mutable variable) is appropriate in your case.
The error you get is absolutely correct and expected.
what's happening here?
When you declare a read-only variable without initializing it you have to make sure that each execution path will have a value in this read-only variable. It means that Kotlin makes sure if your read-only variable was or was not initialized in every place it is used and raises errors if the variable is used inappropriately.
Here you have only one execution path as there are no when or if statements that can split execution into several possible paths.
class Solution {
fun love(){
val message : String
message = "love" // Kotlin knows that `message` was not yet initialized
message = "hate" // Kotlin knows that `message` was yet initialized! It does not allow to modify the value.
}
}
Here is what Kotlin documentation says:
... it is also possible (but discouraged) to split the declaration and the initial assignment, and even to initialize in multiple places based on some condition. You can only read the variable at a point where the compiler can prove that every possible execution path will have initialized it. If you're creating a read-only variable in this way, you must also ensure that every possible execution path assigns to it exactly once.
Example of an execution path
Using when or if statement you create two or more execution paths. Execution paths can be presented as a graph, I'll use #number as a node number. Example:
class Solution {
fun love(){
// #1
val message : String
if (System.currentTimeMillisec() % 2 == 0) {
message = "Not empty"
// #2
}
if (message.isEmpty) { // Error! Message could be not initialized at this point!
println("Empty message")
// #3
}
}
}
Looking at this example, that does not compile, we can calculate at least 3 execution paths.
#1 (none of the if statements was entered. All conditions are false)
#1 -> #2
#1 -> #3
Kotlin can calculate these paths and check if the message variable is initialized in every path it is used. As we can see, as soon as you reach the evaluation of the second if statement (in case of first and third paths) your program will crash because the message has no value. It has no address in memory and a computer which runs this program does not know how to get a value from an address that does not exist.
Now, let's modify this code to make it work:
class Solution {
fun love(){
// #1
val message : String
if (System.currentTimeMillisec() % 2 == 0) {
message = "Not empty"
// #2
} else {
message = ""
// #3
}
if (message.isEmpty) { // Error! Message could be not initialized at this point!
println("Empty message")
// #4
}
}
}
Execution paths:
#1 -> #2
#1 -> #3 -> #4
In this example, Kotlin is sure that the message read-only variable is initialized because there is a 100% chance that one of node 2 or node 3 will be executed. Right after the line where the message gets its initial value (initialized) Kotlin treats this variable as a read-only variable with a value.
Questions are welcome. I will try to simplify this answer.
I want to handle validation in functional approach. I have user validation logic as below
if user object is null throw an exception
if the user is not active throw an exception
if the user type is super do nothing. If user type is admin do some operation.
In my code snippet I am throwing an exception if generated random number is even, otherwise complete the flow.
* You can edit, run, and share this code.
* play.kotlinlang.org
*/
import java.time.Instant
import java.util.UUID
import java.lang.IllegalStateException
import java.util.Random
fun main() {
val user : User? = User(UUID.randomUUID(),Instant.now(),UserStatus.ACTIVE,UserType.SUPER)
//val user : User? = User(UUID.randomUUID(),Instant.now(),UserStatus.ACTIVE,UserType.NORMAL) // works fine
user?.let{ existing -> existing.takeIf{it.status == UserStatus.ACTIVE}?.let{ activeUser ->
activeUser.takeUnless{ user -> user.userType == UserType.SUPER}?.let{
val number = Random().nextInt(5);
println(number)
if(number %2 == 0) throw IllegalStateException("invalid random number")
}
}?: throw IllegalStateException("User is not active right now")
} ?:throw IllegalStateException("user not created at all")
}
data class User(val uuid:UUID, val created:Instant, val status:UserStatus, val userType:UserType)
enum class UserStatus {
ACTIVE,INACTIVE
}
enum class UserType{
SUPER,NORMAL
}
when generated random number is odd its throwing an error message user is not active right now which is not correct. it should silently complete the function call.Any one help me what's wrong with the code?
In the inner block
existing.takeIf { it.status == UserStatus.ACTIVE }?.let { activeUser ->
activeUser.takeUnless { user -> user.userType == UserType.SUPER }?.let {
val number = Random().nextInt(5);
println(number)
if (number % 2 == 0) throw IllegalStateException("invalid random number")
}
} ?: throw IllegalStateException("User is not active right now")
You have a not null value.
Since you're not chaining the ?. calls. The better idea is to do early return/throw.
val existing = user ?: throw IllegalStateException("user not created at all")
So the nested block can be flattened.
The second exception "User is not active right now" happens when one of the two checks is not met.
The programming concept to describe this kind of business logic is not nullable variables. A regular old if check is much cleaner.
if (existing.status != UserStatus.ACTIVE || existing.userType == UserType.SUPER) {
throw IllegalStateException("User is not active right now")
}
BTW your error message seems wrong.
By "functional approach", I think you mean having the code expression-based. But nullable type is not powerful enough for your use case.
You should be looking for Either or Try. Both of them are available in the Arrow library. They are both monads. Their chaining (flatMap), like the ?.let calls, also require the callback hell.
In some languages, there is syntactic sugar to flatten the callback hell of flatMaps. You can take a look at the Monad Comprehensions, and see if you like that.
If not, no problem. In Kotlin, there is no shame associated with early return/throw.
Be pragmatic.
In below code I want to neutralize the throw and continue the method - Can it be done ?
public class TestChild extends TestParent{
private String s;
public void doit(String arg) throws Exception {
if(arg == null) {
Exception e = new Exception("exception");
throw e;
}
s=arg;
}
}
The net result should be that, in case of the exception triggered (arg == null)
throw e is replaced by Log(e)
s=arg is executed
Thanks
PS : I can 'swallow' the exception or replace it with another exception but in all cases the method does not continue, all my interventions take place when the harm is done (ie the exception has been thrown)
I strongly doubt that general solution exists. But for your particular code and requirements 1 and 2:
privileged public aspect SkipNullBlockAspect {
public pointcut needSkip(TestChild t1, String a1): execution(void TestChild.doit(String))
&& this(t1) && args(a1) ;
void around(TestChild t1, String a1): needSkip(t1, a1){
if(a1==null) //if argument is null - doing hack.
{
a1=""; //alter argument to skip if block.
proceed(t1, a1);
t1.s=null;
a1=null; //restore argument
System.out.println("Little hack.");
}
else
proceed(t1, a1);
}
}
I think that generally what you want makes no sense most cases because if an application throws an exception it has a reason to do so, and that reason almost always includes the intention not to continue with the normal control flow of the method where the exception was thrown due to possible subsequent errors caused by bogus data. For example, what if you could neutralise the throw in your code and the next lines of code would do something like this:
if(arg == null)
throw new Exception("exception");
// We magically neutralise the exception and are here with arg == null
arg.someMethod(); // NullPointerException
double x = 11.0 / Integer.parseInt(arg); // NumberFormatException
anotherMethod(arg); // might throw exception if arg == null
Do you get my point? You take incalculable risks by continuing control flow here, assuming you can at all. Now what are the alternatives?
Let us assume you know exactly that a value of null does not do any harm here. Then why not just catch the exception with an after() throwing advice?
Or if null is harmful and you know about it, why not intercept method execution and overwrite the parameter so as to avoid the exception to begin with?
Speculatively assuming that the method content is a black box to you and you are trying to do some hacky things here, you can use an around() advice and from there call proceed() multiple times with different argument values (e.g. some authentication token or password) until the called method does not throw an exception anymore.
As you see, there are many ways to solve your practical problem depending on what exactly the problem is and what you want to achieve.
Having said all this, now let us return to your initial technical question of not catching, but actually neutralising an exception, i.e. somehow avoiding its being thrown at all. Because the AspectJ language does not contain technical means to do what you want (thank God!), you can look at other tools which can manipulate Java class files in a more low-level fashion. I have never used them productively, but I am pretty sure that you can do what you want using BCEL or Javassist.