I have a bunch of tasks that need to be performed before and after a method call.
My approach is to use annotation processing, and a custom annotation to do this.
#BeforeSomething(task = 'something')
#AfterSomething(task = 'something else')
fun something(blah: Something) {
Log.d("Something", "Something $blah")
}
So that the end output from just running this method could be like:
Before task, options 'something'
Something blahblahblah
After task, options 'somethign else'
Whats the best way to do this? I've looked into AspectJ but thats not working with Kotlin. I've also looked into Lombok, but I can't use that library.
You could provide the tasks via a higher order function - a wrapper function:
inline fun <T> wrapFunction(before: Task? = null, after: Task? = null, body: () -> T): T =
try {
before?.run()
body()
} finally {
after?.run()
}
fun something(blah: String) = wrapFunction(before = BeforeTask, after = AfterTask) {
println("Something")
}
This requires no extra tools and is refactoring safe.
If you not provide a before or after parameter the Kotlin compiler will not even generate the null-check, due to dead code elimination (abstractions without costs).
Related
I am currently working on a project where I need to create a loop using spring webflux to generate a Flux for downstream processing. The loop should sequentially take batches of elements from a source (in this instance a repository) and pass the elements as signal in a Flux. To acquire the elements, we have a repository method which fetches the next batch. When all elements have been processed, the method yields an empty List.
I have identified that I can use Flux::generate in the following manner:
Flux.<List<Object>>generate(sink -> {
List<Object> batch = repository.fetch();
if (batch.isEmpty()) {
sink.complete();
} else {
sink.next(batch);
}
})
...
However, when I use this, the argument method runs continuously, buffering until I run out of memory.
I have also tried using Flux::create, but I am struggling to find an appropriate approach. I have found that I can do the following:
Consumer<Integer> sinker;
Flux<?> createFlux() {
return Flux.<List<Object>>create(sink -> sinker = integer -> {
List<Object> batch = repository.fetch();
if (batch.isEmpty()) {
sink.complete();
} else {
sink.next(batch);
}
})
...
.doOnNext(x -> sinker.accept(y))
...
}
Then I just need to call the Consumer initially to initiate the loop.
However, I feel like I am overly complicating a job which should have a fairly standard implementation. Also, this implementation requires secondary calls to get started, and I haven't found a decent way to initiate it within the pipeline (for instance, using .onSubscribe() doesn't work, as it attempts to call the Consumer before it has been assigned).
So in summary, I am looking for a simple way to create an unbounded loop while controlling the backpressure to avoid outOfMemory-errors.
I believe I have found a simpler solution which serves my need. The method Mono::repeat(BooleanSuplier) allows me to loop until the list is empty, simply by:
Mono.fromCallable(() -> repository.nextBatch())
.flatMap(/* do some stuff here */)
.repeat(() -> repository.hasNext())
If other more elegant solutions exist, I am still open for suggestions.
TL;DR:
Is there a simple syntax in java to access kotlins backticked functions such as fun `if`(){...}
Long Version:
In Kotlin, one may write the following class.
class ShapeShifter {
fun speak() { println("Hello fellow hooman") }
fun `speakLikeA🐱`() { println("Meow") }
fun `speakLikeA🐶`() { println("Bwoof !") }
fun `speakLikeA🐧`() { println("NOOT NOOT ! (you would'nt have so much problems with linux ...)") }
}
And this would work just fine ... All your fellow kotlin-ers would be able to speak to you in all your forms like so :
ShapeShifter().`speakLikeA🐶`() // would work like a charm
but when interracting with java-ist muggles your secret identity would be safe because I am pretty sure java-ists can only interract with you like so :
new ShapeShifter().speak()
My question is :
Is there a way for java commoners to reach to your backticked kotlin functions WITHOUT resorting to using black magics such as introspection/reflection like so :
var tomJedusor = new ShapeShifter();
ShapeShifter.class.getDeclaredMethod("speakLikeA🐍").invoke(tomJedusor); //the forbidden arcane spell banned from Konoha ...
#JvmName annotation lets you define jvm name for that method.
#JvmName("speakLikeACat")
fun `speakLikeA🐱`() { println("Meow") }
#JvmName("speakLikeADog")
fun `speak like a 🐶`() { println("Bwoof") }
Now, you can access that method from java code.
// .java
shapeShifter.speakLikeACat();
shapeShifter.speakLikeADog();
Accesing backticked kotlin methods, fields from java is only possible with some jvm annotations. Kotlin compiler must obey java language specifications to generate the byte code. Actually java language specification does not allow this feature.
I have a Kotlin library that I'm attempting to call from Java. I haven't worked with Kotlin before.
The Kotlin library function is as follows:
suspend fun decode(jwt: String): UsefulThing {
// does a bunch of stuff, removed for brevity.
return otherthing.getUsefulThing(jwt)
}
How can I call this from Java? So far I've tried:
Continuation<UsefulThing> continuation = new Continuation<>() {
#NotNull
#Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
#Override
public void resumeWith(#NotNull Object o) {
System.out.println("Result of decode is " + o);
}
};
// Call decode with the parameter and continuation.
Object result = UsefulThingKt.decode(JWT, continuation);
// result is COROUTINE_SUSPENDED
I never see any console output. Looks like the continuation is never called, or it's run in another context. I've pored over other answers and coroutines seem to have gone through a number of iterations - I can't find an explanation that really makes sense to me.
I should note that I'm running on Java 11.
How can I simply call the kotlin function?
I suggest to not even try. Suspend functions were never meant for Java interop.
Instead, convert it on the Kotlin side to something that Java understands - to CompletableFuture:
fun decodeAsync(jwt: String): CompletableFuture<UsefulThing> = GlobalScope.future { decode(jwt) }
We can freely mix Java and Kotlin code in a single module, so you can create such wrapper inside your project.
Depending on your case you could use GlobalScope (in Java we don't have structured concurrency) or you could create a custom CoroutineScope and handle its lifecycle manually.
There are so many questions and threads about Optional API but I didn't found any for my case.
For example, I need first check emptiness of an Optional variable, for logging purpose, and then
check the value, if presents, with some predicate. Whatever check fails, I need to throw an exception.
Below is my real workaround for that
SomeValue value = someOptional.orElseThrow(() -> {
log.debug("nothing here");
return new NothingSpecialHereException();
});
if (!value.isSpecial()) {
log.debug("something here, but not special");
throw new NothingSpecialHereException();
}
When I was looking for an alternative solution for that, I tried something like that
SomeValue value = someOptional
.filter(SomeValue::isSpecial)
.orElseThrow(() -> {
log.debug("nothing special here"); // but this case for both "no value" and "value is not special"
return new NothingSpecialHereException();
});
I know that there's no any built-in solution for this situation in Java, but seems like I missing something like:
SomeValue value = someOptional
.ifNotPresent(() -> log.debug("nothing here")) // that method returns Optional for further invocatons
.filter(val -> {
if (!val.isSpecial()) {
log.debug("something here, but not special");
return false;
}
return true;
})
.orElseThrow(NothingSpecialHereException::new);
This is not the first time when I miss something like ifNotPresent or else* methods for use in the middle of pipe, not in the end. IMO sometimes that approach might be more readable, for example
optional
.map(...)
.filter(...)
.ifEmpty(...) // do smth after filter, maybe even throw
.map(...) // and continue processing
Maybe anyone encountered same problem? Or maybe I missed some better solution? Maybe there's a library that provides solutions for that?
The JDK Optional includes (since Java 9 only, which was a major oversight in Java 8) ifPresentOrElse, which could be used with a no-op first argument. Alternately, the Vavr library is a set of functional wrappers that is slightly more consistent than Optional and provides additional useful wrappers such as Try at the cost of having an extra dependency.
SomeValue value = Optional.ofNullable(someValue.isPresent()?
someValue.filter(SomeValue::isSpecial)
.orElseThrow(NothingSpecialHereException::new)
:null)
.orElseThrow(NothingHereException::new) ;
If someValue is present then the value can be set to special value or throw exception NothingSpecialHereException else if someValue is not present it will give Optinal it will check if null (someValue is not present) Throw NothingHereException.
Exception "NothingSpecialHereException" thrown only if value is present and not special.
Exception "NothingHereException" thrown only if value is not present.
Thanks to comments! I've checked out Vavr Option and Guava Optional APIs.
Guava Optional is even less rich in methods than Java Optional, so this is not a solution.
What I've found is the Vavr onEmpty method of Option class, that executes Runnable if the Option value is empty.
SomeValue value = Option.ofOptional(someOptional)
.onEmpty(() -> log.debug("nothing here"))
.filter(val -> {
if (!val.isSpecial()) {
log.debug("something here, but not special");
return false;
}
return true;
})
.getOrElseThrow(NothingHereException::new);
That example displays one of debug messages and throws exception only in one place. Looks a lot better.
However, Vavr is a big library with own collections, functional interfaces and monads like above. This is a big dependency for a project and it is hardly will be implemented in my project or any other long-time-existed project just for new fancy Optional replacement.
Plus, Vavr's Option does'nt have something like onEmptyThrow, so it's not so perfect =).
This thread is still open for suggestions, but now for Java 8 workaround I think this comment may be considered as answer.
UPDATE
Seems like I've found the best solution for problems like that.
Few days ago I found lombok's #ExtensionMethod annotation that makes possible solutions like below
class Extensions {
public static <T, E extends Throwable> Optional<T> ifNotPresentThrow(Optional<T> opt, Supplier<E> exceptionSupplier) throws E {
if (!opt.isPresent()) {
throw exceptionSupplier.get();
}
return opt;
}
}
#ExtensionMethod(Extensions.class)
class Test {
public static void main(String[] args) {
// assume there is someNullable variable
// instead of writing
Extensions.ifNotPresentThrow(Optional.ofNullable(someNullable), NothingHereException::new)
.map(...);
// I can write
Optional.ofNullable(someNullable)
.ifNotPresentThrow(NothingHereException::new)
.map(...); // now possible to continue if optional present
}
}
With that Kotlin-like extension methods it's possible to add any method to Optional's API. Lombok will just transform second variant into first during compilation.
However, this feature is experimental (seems like forever) and has possible bugs and decompiled bytecode looks pretty dirty (with various unnecessary local variables, etc.)
I'm using TestNG for my unit tests and I'd like to check exception messages. OK, #Test(expectedExceptionsMessageRegExp = ...) is exactly what I need, right? Well, at the same time I'd like to externalize my messages so they aren't mixed with my code. I'm loosely following a guide by Brian Goetz, so my exception code looks like
throw new IllegalArgumentException(MessageFormat.format(
EXCEPTIONS.getString(EX_NOT_A_VALID_LETTER), c));
Works perfectly for me, except these two things don't exactly mix. I can't write
#Test(dataProvider = "getInvalidLetters",
expectedExceptions = {IllegalArgumentException.class},
expectedExceptionsMessageRegExp = regexize(EXCEPTIONS.getString(EX_NOT_A_VALID_LETTER)))
Here, regexize is a function that is supposed to replace {0}-style placeholders with .*. However, this fails with a “element value must be a constant expression”. Makes sense, since it's needed at compile time. But what are possible workarounds?
I can imagine a test code generator that would replace these constructs with real message regexps, but it would be a pain to integrate it with IDE, SCM, build tools and so on.
Another option is to use try-catch and check exception message manually. But this is ugly.
Lastly, I think it should be possible to hack TestNG with something like
#Test(expectedExceptionsMessageBundle = "bundle.name.goes.here",
expectedExceptionsMessageLocaleProvider = "functionReturningListOfLocales"
expectedExceptionsMessageKey = "MESSAGE_KEY_GOES_HERE")
This would be a great thing, really. Except that it won't be the same TestNG that Maven fetches for me from the repo. Another option is to implement this, contribute a patch to TestNG and wait for it to be released. I'm seriously considering this option now, but maybe there's an easier way? Haven't I missed something obvious? I can't possibly be the only one with this issue!
Or maybe I'm externalizing my messages in a wrong way. But a guy like Brian Goetz can't be wrong, now can he? Or did I get him wrong?
Update
Based on the answer given here, I've made a tutorial on the topic, covering some pitfalls, especially when using NetBeans 8.1.
Why not using an annotation transformer here?
You will be able to do something like:
#LocalizedException(expectedExceptionsMessageBundle = "bundle.name.goes.here",
expectedExceptionsMessageLocaleProvider = "functionReturningListOfLocales"
expectedExceptionsMessageKey = "MESSAGE_KEY_GOES_HERE")
#Test(dataProvider = "getInvalidLetters",
expectedExceptions = {IllegalArgumentException.class)
public void test() {
// ...
}
Where the annotation transformer will look like:
public class LocalizedExceptionTransformer implements IAnnotationTransformer {
public void transform(ITest annotation, Class testClass,
Constructor testConstructor, Method testMethod) {
if (testMethod != null) {
LocalizedException le = testMethod.getAnnotation(LocalizedException.class);
if (le != null) {
String regexp = regexize(le);
annotation.setExpectedExceptionsMessageRegExp(regexp);
}
}
}
}