invoking scala generic classes from java looses generic info - java

trying to use in java class defined in scala library.
Definition:
final class ScenarioBuilder(...) extends StructureBuilder[ScenarioBuilder]
trait StructureBuilder[B <: StructureBuilder[B]] extends Execs[B] with Feeds[B]
yes, it is Gatling.
my java code:
public ScenarioBuilder callMyApi(ScenarioBuilder in) {
return in.feed(myFeeder.asScala())
.exec(addCookie(Cookie("key", "value")).asScala())
.exec( http("my api call")
.get("/api").asScala());
}
Idea finds this code correct, but whenever i try to compile it i get this errors:
symbol: method exec(io.gatling.core.action.builder.ActionBuilder)
location: class java.lang.Object
or
java: incompatible types: java.lang.Object cannot be converted to io.gatling.core.structure.ScenarioBuilder
in other words methods B feed(...) from Feeds[B] and B exec(...) from Execs[B] always return Object and i would expect B to be ScenarioBuilder
the only way to compile and run this code i came up with is this:
public ScenarioBuilder callMyApi(ScenarioBuilder in) {
StructureBuilder<ScenarioBuilder> sb = in;
sb = (StructureBuilder<ScenarioBuilder>) sb.feed(myFeeder.asScala())
.exec(addCookie(Cookie("key", "value")).asScala());
return (ScenarioBuilder) sb.exec( http("my api call")
.get("/api").asScala());
}
and it looks disgusting
questions:
why does java misses generic info from scala classes?
is there better way to utilise scala Gatling api in java?
why does IDE finds the first variant correct and compiler does not?
yes, i know there's java api in Gatling though by some reason method make is defined like this:
public ScenarioBuilder make(
Function<io.gatling.core.structure.ScenarioBuilder, io.gatling.core.structure.ScenarioBuilder>
f) {
return new ScenarioBuilder(f.apply(wrapped));
}
and to me it is very open question why it is defined this way.
thanks in advance
UPD:
java- 1.8, scala- 2.11.8
myFeeder = listFeeder(Arrays.asList(
Collections.singletonMap("items", Arrays.asList("Item 1", "Item 2"))
)).circular()
Gatling version - 3.9.0 though the question not about Gatling itself it is mostly about calling scala code with generics from java.
By some reason javac does not see generic info in scala classes, and i wonder why.
As to Gatling i've solved the issue by proper API usage, long story short don't use make methods for construction scenario from different steps exec(ChainBuilder) is the choice.

Disclaimer: Gatling's author here
is there better way to utilise scala Gatling api in java?
Don't. Gatling's Java DSL wasn't built with interoperability with the Scala DSL in mind. In particular, asScala() is an internal.
Go with either one or the other, but don't mix them together.

Related

How can one use ScalaTest in a Java test file?

It is possible to use ScalaTest in a Java test file, and if so where can I find examples?
When I try something like:
// MyUTest.java
import org.scalatest.flatspec.AnyFlatSpec;
import org.scalatest.matchers.should.Matchers;
public class MyUTest extends AnyFlatSpec, Matchers {
...
}
I get an error that equal(Object) in Matchers clashes with the same method in matchers.dsl.MatherWords
TL;DR: You cannot do what you are trying.
As stated in Using Scala traits with implemented methods in Java:
From Java perspective Trait.scala is compiled into Trait interface. Hence implementing Trait in Java is interpreted as implementing an interface - which makes your error messages obvious. Short answer: you can't take advantage of trait implementations in Java, because this would enable multiple inheritance in Java (!)
and Matchers is a trait. However, to overcome this issue, you can just remove the Matchers extension, and have the test class:
import org.scalatest.flatspec.AnyFlatSpec;
public class MyUTest extends AnyFlatSpec {
}
Which will compile. Having said that, it will be really hard to actually use the the ScalaTest functionality in Java. For example, a simple test class will be:
public class MyUTest extends AnyFlatSpec {
it should "test1" in { println("test1") }
}
The word should above, is declared at AnyFlatSpecLike, which is trait as well. So you cannot really use it. So I am not really sure how you can overcome this issue, as this is the very basic example that you can find in ScalaTest quick start.
After the above analysis, I think it's going to be really difficult to use ScalaTest in Java. What you can easily do, is the other way around. If you already support Scala, and you have ScalaTest, you can just test the java code in Scala. It is a bit less "organized" as you'd expect to see the java test classes under the java folder, which we just proved impossible. I think having this "mess" is the best solution in such structure.
I totally agree with #Tomer Shetah. I would like to add that you can create wrapper for java on scala:
class JavaScalaTestWrapper extends AnyFunSpec with Matchers {
def println(x : scala.Any) = Predef.println(x)
def shouldEqual(x : scala.Int, ) = SomeCode.someFunc(x) shouldBe s"${x}"
}
And after that you can extend all java test classes through this wrapper:
public class SomeOperationTestJava extends JavaScalaTestWrapper {
#Test
void someOperation() {
SomeOperation so = new SomeOperation();
println("=== test ===");
assert(("test").equals(so.someOperation()));
shouldEqual(3);
}
}
And all scala styled code you can put in wrapper, and after that use these methods from original java code, like additional workaround.

How to access constants defined in java interface from kotlin interface

Why accessing constants defined in java interface from kotlin interface is not allowed or at least I am not able to access it. Is there any other way?
(Yes,I know The constant interface pattern is a poor use of interfaces from Effective Java book)
Following code (java) compiles
// ==== JSubsystem.java ====
public interface JSubsystem {
String IRIS = "IRIS";
String TCS = "TCS";
// ...
}
// ==== JComponentType.java ====
public interface JComponentType {
String HCD = "HCD";
String Assembly = "Assembly";
// ...
}
interface Demo extends JSubsystem {
default void foo() {
System.out.println(IRIS);
}
}
But in the following kotlin interface which extends from java, IRIS|TCS|HCD is not accessible
// ==== AllModels.kt ====
interface AllModels : JSubsystem, JComponentType {
fun foo() = println(IRIS)
}
More context on why we ended up at this situation:
We have a large scala codebase, all the models provide java and scala access.
Now we have scripting requirement for 1-5% of our users where we have utilised kotlin's scripting (.kts), dsl, coroutines and suspension features
We have provided script construct inside which users will have access to complete DSL and all the models.
We do not want users to explicitly import models from different files and we do not want to repeat defining models again in kotlin.
One of the solution we thought could work in this case is having java models (these are simple delegations to scala models) in interface and then have one interface at kotlin side which extends from all these java model interfaces and then script can be receiver of this interface - AllModels
You need to specify interface explicitly:
fun foo() = println(JSubsystem.IRIS)
Or you can import constant explicitly:
import your.package.name.JSubsystem.IRIS
You can customize your script environment as described in https://github.com/Kotlin/KEEP/blob/master/proposals/scripting-support.md, in particular you can add imports which will be automatically available with defaultImports.
This example in the Kotlin discussion forum should be helpful:
First, you need to create a script definition - a separate jar that describes your script “template”, e.g. similar to the https://github.com/JetBrains/kotlin/tree/master/libraries/tools/kotlin-main-kts
Your definition may look something like:
#KotlinScript(fileExtension = "custom.ext", compilationConfiguration = ScriptConfiguration::class)
abstract class MyScript(val bindings: Map<String, Any?>) {
val ortResult = bindings["ortResult"] as OrtResult
val evalErrors = mutableListOf<OrtIssue>()
}
object ScriptConfiguration : ScriptCompilationConfiguration(
{
defaultImports("com.here.ort.model.*", "java.util.*")
ide {
acceptedLocations(ScriptAcceptedLocation.Everywhere)
}
})
It is a good idea to have a dedicated extension for your scripts (“custom.ext” in the example above), since IDE distinguish scripts by the extension.
Then you’ll need to create your own JSR-223 factory the same way as here - https://github.com/JetBrains/kotlin/blob/master/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/jsr223/KotlinJsr223ScriptEngineFactoryExamples.kt#L28, but use your script definition (MyScript) in place of KotlinStandardJsr223ScriptTemplate. You probably can do it in the same jar. And you need to register it in the services folder, of course.
You’ll still need a postface part in your evaluator though, but it seems not relevant to the IDE.
Then finally you need to supply Intellij with the definition. The simplest ad-hoc way to do it is to specify the FQN of your definition class along with the classpath needed to load it in the kotlin compiler settings -> “Kotlin scripting” in Intellij.

C# extension methods in Java using Scala

I need to create some extension methods in my Java code. I've read some posts here in SO and people suggest XTend or Scala in order to achieve this.
Now, my question would be.. if i write kind of an Adapter layer in Scala (adding there my extension methods) and then using that project as a dependency for my own Java project, are those extended methods available for me to use, or they are defined just for the 'scope of Scala project' and then the JVM output cannot provide those new methods to the other project using it?
EDIT:
What i need to do is to extend a full hierarchy of classes in a given library and give some new functionality. As for Java's first approach I should extend every class in that hierarchy creating my own hierarchy of extended classes adding the new method there. I would like to avoid this and give the final user the sense of native functionality in the original hierarchy.
Regards.
As mentioned above in the comments, it is very close to C# but not exactly there because of the type erasure. For example, this works fine:
object myLibExtensions {
implicit class TypeXExtension( val obj: TypeX ) extends AnyRef {
def myCustomFunction( a: String ): String = {
obj.someMethod(a)
}
}
}
It will act somewhat similar to C# extension methods, i.e. create static method wrappers in reasonable cases (but not always).
The only thing I am missing in Scala is that you can't (or at least I couldn't figure out how to) return the values of the types being extended. For example, assume I want to have something like an extension method "withMeta" that works as follows:
class TypeY extends TypeX { def methodOfY(...) ...}
var y: TypeY = ....
y.withMeta(...).methodOfY(...)
The following didn't work for me:
object myLibExtensions {
private val something = ....
implicit class Extension[T<:TypeX]( val obj: T ) extends AnyRef {
def withMeta( meta: Meta[T] ): T = {
something.associateMeta(obj,meta)
val
}
}
}
... because T is being erased to TypeX. So effectively you will have to write extensions for all specific leaf classes of the hierarchy in this case, which is sad.

Is it possible to use a Java 8 style method references in Scala?

I'm developing a JavaFX8 application in Scala but I couldn't figure out how to pass a method reference to an event handler. To clarify, I'm not using ScalaFX library but build my application directly on top of JavaFX.
Here's the related code snippet.
InputController.java (I wrote this test class in Java to isolate the issue to consume a method reference only)
public class InputController {
public void handleFileSelection(ActionEvent actionEvent){
//event handling code
}
public InputController() {
//init controller
}
}
This works (Java)
InputController inputController = new InputController();
fileButton.setOnAction(inputController::handleFileSelection);
This doesn't work (Scala)
val inputController = new InputController
fileButton.setOnAction(inputController::handleFileSelection)
Here's the error message from the compiler (Scala 2.11.6).
Error:(125, 45) missing arguments for method handleFileSelection in class Main;
follow this method with '_' if you want to treat it as a partially applied function
fileButton.setOnAction(inputController::handleFileSelection)
^
If I use Scala 2.12.0-M2 instead, I get a different error message.
Error:(125, 45) missing argument list for method handleFileSelection in class Main
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `handleFileSelection _` or `handleFileSelection(_)` instead of `handleFileSelection`.
fileButton.setOnAction(inputController::handleFileSelection)
^
Is there a native way which Scala can leverage method references introduced in Java 8? I'm aware of the implicit conversions approach to use a lambda expression but I want to know if there is a way to use a method reference similar to Java 8 without needing to use the lambda decleration.
inputController::handleFileSelection is Java syntax, which isn't supported or needed in Scala because it already had a short syntax for lambdas like this: inputController.handleFileSelection _ or inputController.handleFileSelection(_) (inputController.handleFileSelection can also work, depending on the context).
However, in Java you can use lambdas and method references when any SAM (single abstract method) interface is expected, and EventHandler is just such an interface. In Scala before version 2.11 this isn't allowed at all, in 2.11 there is experimental support for using lambdas with SAM interfaces, which has to be enabled using -Xexperimental scalac flag, and starting from 2.12 it is fully supported and doesn't need to be enabled.
You should pass function which applying one parameter of type ActionEvent:
val button = new Button()
val inputController = new InputController()
def handler(h: (ActionEvent => Unit)): EventHandler[ActionEvent] =
new EventHandler[ActionEvent] {
override def handle(event: ActionEvent): Unit = h(event)
}
button.setOnAction(handler(inputController.handleFileSelection))
If you want a method reference that also takes the class instance as a parameter, for instance like String::length in Java, you can do (_:String).length which is equivalent to (s:String) => s.length().
The types of these are in Java Function<String, Integer> and in Scala thus String => Int.
Could you try
fileButton.setOnAction(() => inputController.handleFileSelection())

Understanding generic Java class signature in cross compilation with C#

I have to implement some classes in Java that will pass tests written in C# (using Visual Studio unit tests). I came across problem with this part of test:
var portfolioSignatureAttribute = dllType
.GetCustomAttributes(typeof(SignatureAttribute), false)
.Cast<SignatureAttribute>()
.FirstOrDefault();
Assert.AreEqual("<C::LIConverter<LCurrency;LCurrency;>;>Ljava/util/ArrayList<LBoughtStock;>;Ljava/lang/Iterable<LBoughtStock;>;", portfolioSignatureAttribute.Signature, iterableMessage);
The signature I tried writing was, but doesn't work is:
public class Portfolio extends ArrayList<BoughtStock> implements IConverter<Currency, Currency>, Iterable<BoughtStock>
Also, i got some message thrown by this assertion:
Type Portfolio incorrect signature. Remember to add generic interface java.lang.Iterable implementation. Remember to give a name C to generic parameter. Remember also to inherit your class from java.util.ArrayList.
And here is also fragment from test output:
Result Message: Assert.AreEqual failed.
Expected:<<C::LIConverter<LCurrency;LCurrency;>;>Ljava/util/ArrayList<LBoughtStock;>;Ljava/lang/Iterable<LBoughtStock;>;>.
Actual:<Ljava/util/ArrayList<LBoughtStock;>;LIConverter<LCurrency;LCurrency;>;Ljava/lang/Iterable<LBoughtStock;>;>.
So generally, there is problem with: lack of "C::" and with order of those attributes. I totally cannot find anywhere any informations about such things, and how to understand it. And the question is - what the class signature should be?
Thanks for help!
I managed to solve the problem (by myself, i am proud :) )
The signature should be:
public class Portfolio<C extends IConverter<Currency, Currency>> extends ArrayList<BoughtStock> implements Iterable<BoughtStock> {

Categories

Resources