Kotlin vs Java nested generics - java

I'm having some trouble when trying to translate Java code with nested generics to Kotlin. Take this Java SSCCE as an example (please note the relation between S and T):
public class JavaTest {
private class JavaObjectContainer<S> {
public S obj;
}
private abstract class JavaSampleClass<S, T extends JavaObjectContainer<S>> {
private Class<S> type;
public JavaSampleClass(Class<S> type) {
this.type = type;
}
public Class<S> getType() {
return type;
}
public abstract void callMethod(S s);
}
private class JavaChildSampleClass extends JavaSampleClass<String, JavaObjectContainer<String>> {
public JavaChildSampleClass() {
super(String.class);
}
#Override
public void callMethod(String s) {}
}
private class JavaTestContainer {
private Map<Class<?>, JavaSampleClass> sampleClasses;
public JavaTestContainer() {
this.sampleClasses = new HashMap<>();
}
public void registerJavaSampleClass(JavaSampleClass javaSampleClass) {
sampleClasses.put(javaSampleClass.getType(), javaSampleClass);
}
public void callMethod(Object obj) {
sampleClasses.get(obj.getClass()).callMethod(obj);
}
}
public void test() {
JavaTestContainer javaTestContainer = new JavaTestContainer();
javaTestContainer.registerJavaSampleClass(new JavaChildSampleClass());
javaTestContainer.callMethod("Hola");
}
}
Think of this SSCCE as an implementation of a generic factory pattern, where the user registers multiple JavaSampleClass whose methods can be invoked in the future.
As Kotlin does not provide an alternative to wildcards, I have tried the following approach:
class KotlinTest {
private class KotlinObjectContainer<S> {
var obj : S? = null
}
private open class KotlinSampleClass<S, T : KotlinObjectContainer<S>>(var type: Class<S>) {
fun callMethod(s : S) {}
}
private class KotlinChildSampleClass : KotlinSampleClass<String, KotlinObjectContainer<String>>(String::class.java)
private inner class KotlinTestContainer {
private val sampleClasses: MutableMap<Class<Any>, KotlinSampleClass<Any, KotlinObjectContainer<Any>>> = mutableMapOf()
fun registerKotlinSampleClass(kotlinSampleClass: KotlinSampleClass<Any, KotlinObjectContainer<Any>>) {
sampleClasses.put(kotlinSampleClass.type, kotlinSampleClass)
}
fun callMethod(obj : Any) {
sampleClasses[obj.javaClass]?.callMethod(obj)
}
}
fun test() {
val kotlinTestContainer = KotlinTestContainer()
// Exception!
kotlinTestContainer.registerKotlinSampleClass(KotlinChildSampleClass())
kotlinTestContainer.callMethod("Hello")
}
}
The above code throws the following exception in the IDE:
Type mismatch.
Required: KotlinTest.KotlinSampleClass<Any, KotlinObjectContainer<Any>>
Found: KotlinTest.KotlinChildSampleClass
I have been thinking of declaring sampleClasses map as
MutableMap<*, *>
But then, how can I initialize it? Also, as * represents an out-projected parameter, the IDE shows me an error when trying to put new values in the map.
How can I overcome this issue? I'm quite certain that I'm missing something...

As Kotlin does not provide an alternative to wildcards...
I have been thinking of declaring sampleClasses map as
MutableMap<*, *>
For this case * corresponds to wildcards perfectly well. If you have Class<?> in Java, you want Class<*> in Kotlin, not Class<Any> or *.
private val sampleClasses: MutableMap<Class<*>, KotlinSampleClass<*, *>> = mutableMapOf()
fun registerKotlinSampleClass(kotlinSampleClass: KotlinSampleClass<*, *>) {
sampleClasses.put(kotlinSampleClass.type, kotlinSampleClass)
}
#Suppress("UNCHECKED_CAST")
fun callMethod(obj : Any) {
(sampleClasses[obj.javaClass] as KotlinSampleClass<Any, *>?)?.callMethod(obj)
}
The only reason you don't need the cast in callMethod in Java is because you are using raw types (as Turing85's comment mentions) and the compiler basically gives up on type checking.

Related

JAVA How can i get a method to accept a parent class and all of it's extended classes?

I apologize if this has been answered before but either i don't know the correct verbiage or my google fu is bad.
I have a TestModel class which has the getters and setters for all the tests I use. Then I have a AdditionalTestModel class that extends the TestModel with additional getters and setters for that specific type of tests.
Now I have BuildTest Class that i want to be able to pass TestModel and any extended classes of TestModel.
public static Class<?> buildTest(Class<?> test, Class<?> template)
throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Class<?> testClass = test.getClass();
Method[] testMethods = testClass.getMethods();
for (Method method : testMethods) {
String name = method.getName();
if (name.startsWith("get")) {
String testMethodType = method.getReturnType().getTypeName();
// additional code removed//
}
}
If instead of Class<?> i was using TestModel it would work for any test that i pass of Class type TestModel. But i want to be able to pass the extended class to this method as well without having to write a method for each extended class. Any recommendations?
Adding information on the models in case it matters.
public class TestModel {
private String testDescription;
private String testName;
private String apiPath;
private String method;
private String expectedTest;
private Map<String, String> header = new HashMap<>();
private Object body;
private String expectedResult;
private String testCaseId;
private String testUUID;
private List testTypes;
public String getTestDescription() {
return testDescription;
}
public void setTestDescription(String testDescription) {
this.testDescription = testDescription;
}
public String getTestName() {
return testName;
}
public void setTestName(String testName) {
this.testName = testName;
}
public String getAPIPath() {
return apiPath;
}
public void setAPIPath(String apiPath) {
this.apiPath = apiPath;
}
public String getExpectedTest() {
return expectedTest;
}
public void setExpectedTest(String testName) {
this.expectedTest = testName;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public Map<String, String> getHeader() {
return header;
}
public void setHeader(Map<String, String> header) {
this.header = header;
}
public Object getBody() {
return body;
}
public void setBody(Object body) {
this.body = body;
}
public String getExpectedResult() {
return expectedResult;
}
public void setExpectedResult(String expectedResult) {
this.expectedResult = expectedResult;
}
public String getTestCaseId() {
return testCaseId;
}
public void setTestCaseId(String testCaseId) {
this.testCaseId = testCaseId;
}
public String getTestUUID() {
return testUUID;
}
public void setTestUUID(String testUUID) {
this.testUUID = testUUID;
}
public List getTestTypes() {
return testTypes;
}
public void setTestTypes(List testTypes) {
this.testTypes = testTypes;
}
}
public class AdditionalTestModel extends TestModel {
#Override public Object getBody() {
return super.getBody();
}
}
Edit: per a request adding the call information here:
#Test(dataProvider = "Default", threadPoolSize = THREADS, timeOut = API_TIME_OUT)
#Description("")
public void sampleTest(AdditionalTestModel testFromDataProvider) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
testSetup(testFromDataProvider);
AdditionalTestModel test = BuildTest.buildTest(testFromDataProvider, template);
Response response = RestAPI.call(test, testEnvironment);
if (null != response) {
ValidateAPIResponse.validateTestModel(test, response);
} else {
Assert.fail("Response is null, probably a bad method.");
}
}
Where testFromDataProvider is passed from a TestNg data provider.
Now LppEdd below already pointed out i could only assign the base class using generics so working on trying it his way, just have not gotten a chance to change things up yet.
Edit: Also realize now my question was bad. Thanks LppEdd. I should have asked How can I get a method to accept an instance of a class and an instance of any extended class
You are close, you just need to use the extends modifier.
If the class passed in as the test and template parameter should be the same exact class type, you can do:
public static <T extends TestModel> Class<T> buildTest(Class<T> test, Class<T> template) { ... }
Otherwise you can do
public static Class<? extends extends TestModel> buildTest(Class<? extends TestModel> test, Class<? extends String> extends TestModel) { ... }
Which will allow different types to be returned and passed in to each parameter.
You can read up on Java generics and wilcards starting here: https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html
Your buildTest method must accept a TestModel class.
You might be looking for something like
public static TestModel buildTest(
final TestModel test,
final TestModel template) {
final Class<? extends TestModel> testClass = test.getClass();
final Method[] testMethods = testClass.getMethods();
for (final Method method : testMethods) {
final String name = method.getName();
if (name.startsWith("get")) {
final String testMethodType = method.getReturnType().getTypeName();
// additional code removed
}
}
// Maybe
return yourNewInstance; // yourNewInstance is a TestModel, or any class extending it
}
The template argument seems unused here (clarify).
What's the wanted return type? (clarify)
Usage example
final TestModel value1 = buildTest(new TestModel(), ...);
final TestModel value2 = buildTest(new AdditionalTestModel(), ...);
This looks to be exactly the same problem as must be solved by test frameworks. For example, see junit (https://junit.org/junit5/).
The core problem is how to obtain the collection of test methods of a class.
A direct solution would be to have the test class be required to answer its test methods, say, Collection<Function<Void, Void>> getTests(); This has several problems, one being that sub-classes must explicitly list their test methods, two being that sub-classes must be careful to add in the test methods from their super-class, and third, this really fits more as static behavior, which would try to shift java instance typing to the class layer, which just isn't supported by java.
An indirect solution would be to require that test methods satisfy a particular pattern (for example, must start with "test" and have no parameters), and use reflection to discover the methods. Or, use an annotation (say, #Test, which is what junit does) to mark out test methods, and again use the java reflection API to discover methods with the marker.

java.lang.ClassCastException: subclass cannot be cast to superclass

I changed a class that implements a superclass interface that now looks like:
class TimestampConversion(format: String, formatter: SimpleDateFormat) extends ObjectConversion[Any] {
override def execute(input: String): Timestamp = {
// hidden implementation details
}
override def revert(input: Any): String = {
// hidden implementation details
}
override def fromString(input: String): Timestamp = {
// hidden implementation details
}
}
object TimestampConversion {
def apply(format: String): Try[TimestampConversion] = {
// hidden implementation details
new SimpleDateFormat().map(new TimestampConversion(format, _))
}
}
After I changed the code, I started getting cast exceptions when calling this class. This class used to work when it was:
class TimestampConversion(format: String, formatter: SimpleDateFormat) extends ObjectConversion[Any] with FormattedConversion[SimpleDateFormat] {
def this(format: String) = this(format, new SimpleDateFormat(format))
override def execute(input: String): Timestamp = {
// hidden implementation details
}
override def revert(input: Any): String = {
// hidden implementation details
}
override def fromString(input: String): Timestamp = {
// hidden implementation details
}
override def getFormatterObjects: Array[SimpleDateFormat] = {
// hidden implementation details
}
}
The hierarchy of subclasses is TimestampConversion(sub) <-- ObjectConversion <-- NullConversion (super)
When I try calling the calling my newly modified TimestampConversion class, I get this exception
java.lang.ClassCastException:
...TimestampConversion cannot be cast to
...NullConversion
at...my_parser$$anonfun$7.apply(my_parser.scala:65)
at ...my_parser$$anonfun$7.apply(my_parser.scala:59)
where TimestampConversion is called here:
for ((col, i) <- all_cols) {
val converter = col.type match {
// hidden implementation details
case TimestampType => TimestampConversion("some_format") // line 65 in my_parser
case unknown => throw new RuntimeException("Met a bad type")
}
}
and it used to be called like this (when it was working):
for ((col, i) <- all_cols) {
val converter = col.type match {
// hidden implementation details
case TimestampType => new TimestampConversion("some_format")
case unknown => throw new RuntimeException("Met a bad type")
}
}
I've implemented the two #Override methods from the NullConversion interface, and I don't get a warning or anything from my IDE before compiling saying that TimestampConversion isn't fully implementing NullConversion or something. I merely got rid of the association with FormattedConversion. Why am I not able to invoke my class like this? I've also never touched the ObjectConversion and NullConversion classes.
Here are the superclasses' structures:
public abstract class ObjectConversion<T> extends NullConversion<String, T> {
public ObjectConversion() {
super(null, null);
}
public ObjectConversion(T val, String nullVal) {
super(val, nullVal);
}
#Override
public T execute(String input) {
return super.execute(input);
}
protected final T fromInput(String input) {
return fromString(input);
}
protected abstract T fromString(String input);
#Override
public String revert(T input) {
return super.revert(input);
}
#Override
protected final String undo(T input) {
// hidden implementation details
}
public T getValueIfStringIsNull() {
// hidden implementation details
}
public String getValueIfObjectIsNull() {
// hidden implementation details
}
public void setValueIfStringIsNull(T valueIfStringIsNull) {
// hidden implementation details
}
public void setValueIfObjectIsNull(String valueIfObjectIsNull) {
// hidden implementation details
}
}
and this class extends NullConversion<String, T>
public abstract class NullConversion<I, O> implements Conversion<I, O> {
private O valueOnNullInput;
private I valueOnNullOutput;
public NullConversion() {
this(null, null);
}
public NullConversion(O valueOnNullInput, I valueOnNullOutput) {
// hidden implementation details
}
#Override
public O execute(I input) {
// hidden implementation details
}
protected abstract O fromInput(I input);
#Override
public I revert(O input) {
// hidden implementation details
}
protected abstract I undo(O input);
public O getValueOnNullInput() {
// hidden implementation details
}
public I getValueOnNullOutput() {
// hidden implementation details
}
public void setValueOnNullInput(O valueOnNullInput) {
// hidden implementation details
}
public void setValueOnNullOutput(I valueOnNullOutput) {
// hidden implementation details
}
}
Also, here's the class that the old code used to extend FormattedConversion (though I doubt it makes a difference)
public interface FormattedConversion<T> {
T[] getFormatterObjects();
}

Generic types java

Check my Custom class. If I change DoSomething() to
final Custom<User> _custom = new Custom<>(new TypeToken<Custom<User>.Response<User>>(){}, "");
Then the DEBUG log is: "com.application.models.Custom$Response<com.application.models.User>" serverResponse: ""
But if DoSomething is:
final Custom<T> _custom = new Custom<>(new TypeToken<Custom<T>.Response<T>>(){}, "");
The DEBUG log is:
"com.application.models.Custom$Response<T>" serverResponse: "
I have a class like this:
public class Main
{
public static <T> void DoSomething()
{
final Custom<T> _custom = new Custom<>(new TypeToken<Custom<T>.Response<T>>(){}, "");
}
}
// I added the debug log at the right of the variables
public class Custom<T>
{
private TypeToken<Response<T>> _responseType; _response: null
private Response<T> _response; _response: null
private String _serverResponse; _serverResponse = null;
public Custom(TypeToken<Response<T>> responseType, String serverResponse) `responseType: "com.application.models.Custom$Response<T>" serverResponse: "`
{
this._responseType = responseType;
this._serverResponse = serverResponse;
}
public class Response<t>
{
private List<t> data = null;
public List<t> GetData()
{
return this.data;
}
}
}
This is where I call Main..
public class User
{
public int Id;
public void Test()
{
Main.<User>DoSomething();
}
}
Java mostly work with compile time generics, meaning that when you compile then your generics are gone. They are only a compile time check. Does that answer your question? Google Type Erasure.

How to specify object custom serialization in ORMLite?

I would like to store some field of type ParentClass as json string into my database. I don't want to use Serializable interface and DataType.SERIALIZABLE cause it ties with full class name of serialized class.
So I'm using the following code:
class ParentClass {
#DatabaseField(persisterClass = MyFieldClassPersister.class)
private MyFieldClass myField;
}
where persister class a kind of:
public class MyFieldClassPersister extends StringType {
private static final MyFieldClassPersister singleTon = new MyFieldClassPersister();
public static MyFieldClassPersister getSingleton() {
return singleTon;
}
protected MyFieldClassPersister() {
super(SqlType.STRING, new Class<?>[0]);
}
#Override
public Object parseDefaultString(FieldType fieldType, String defaultStr) {
return jsonStringToObject(defaultStr);
}
#Override
public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
String string = results.getString(columnPos);
return jsonStringToObject(string);
}
private static MyFieldClass jsonStringToObject(String string) {
// json to object conversion logic
}
}
Here are two issues I've met:
I didn't get how to specify custom convertion from object to string. Seems that ORMLite calls Object.toString() in order to get string representation of the object. It would be great to have some method in Persister in which I could specify how to convert Object to string (json in my case). Yes, I can override toString() method in MyFieldClass, but it is more convenient to perform conversion in Persister. Is there any method I could override in order to specify convertion from model object to db-object?
If I mark my custom field type as String type:
class ParentClass {
#DatabaseField(dataType = DataType.STRING, persisterClass = MyFieldClassPersister.class)
private MyFieldClass myField;
}
then ormlite crashes when saving object with the following message:
java.lang.IllegalArgumentException: Field class com.myapp.venue.MyFieldClass for
field FieldType:name=myField,class=ParentClass is not valid for type
com.j256.ormlite.field.types.StringType#272ed83b, maybe should be
class java.lang.String
It doesn't crash if I omit dataType specification. Can I avoid this crash in some way? It seems to me that it's better to specify types explicitly.
So basically your persister should be implemented in the next way:
public class MyFieldClassPersister extends StringType {
private static final MyFieldClassPersister INSTANCE = new MyFieldClassPersister();
private MyFieldClassPersister() {
super(SqlType.STRING, new Class<?>[] { MyFieldClass.class });
}
public static MyFieldClassPersister getSingleton() {
return INSTANCE;
}
#Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
MyFieldClass myFieldClass = (MyFieldClass) javaObject;
return myFieldClass != null ? getJsonFromMyFieldClass(myFieldClass) : null;
}
#Override
public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
return sqlArg != null ? getMyFieldClassFromJson((String) sqlArg) : null;
}
private String getJsonFromMyFieldClass(MyFieldClass myFieldClass) {
// logic here
}
private MyFieldClass getMyFieldClassFromJson(String json) {
// logic here
}
}
You should register it in onCreate method of your OrmLiteSqliteOpenHelper class
#Override
public void onCreate(SQLiteDatabaseHolder holder, ConnectionSource connectionSource) {
try {
//...
DataPersisterManager
.registerDataPersisters(MyFieldClassPersister.getSingleton());
} catch (SQLException e) {
// log exception
}
}
And then you can use it in your model like this:
#DatabaseField(persisterClass = MyFieldClassPersister.class, columnName = "column_name")
protected MyFieldClass myFieldClass;
Don't register the persister adapter in the onCreate() method. This method only gets called when your database is first created. You should add this somewhere else, like your constructor or onOpen() method.

Use reflection to create classes at runtime

I have to create a list of objects, which are configured according to the name of some classes received as input.
For each object I have to call a method, which add an operation that is created dynamically.
However I don't know exactly ho to resolve the problem.
Please see an example below.
String className; // this is an input parameter
final Class<?> classType = Class.forName(className);
// here I would like to use classType instead of "?" but it gives me an error.
Task<?> task = TaskFactory.createTask((String)classType.getField("_TYPE").get(null)));
tasks.put(task, null);
task.addOperation(new Operation<classType>() { // this gives an error
#Override
public void onNewInput(classType input) { // this gives an error
System.out.println(input)
}
});
As you can see from the comments, the surrounding infrastructure and the intention are not entirely clear. However, you can achieve a certain degree of type-safety with a "helper" method that captures the type of the given Task, and allows you to work with this type internally:
public class RuntimeType
{
public static void main(String[] args) throws Exception
{
String className = "";
final Class<?> classType = Class.forName(className);
Task<?> task = TaskFactory.createTask((String)classType.getField("_TYPE").get(null));
addOperation(task);
}
private static <T> void addOperation(Task<T> task)
{
task.addOperation(new Operation<T>()
{
#Override
public void onNewInput(T input)
{
System.out.println(input);
}
});
}
}
class TaskFactory
{
public static Task<?> createTask(String string)
{
return null;
}
}
class Task<T>
{
public void addOperation(Operation<T> operation)
{
}
}
interface Operation<T>
{
void onNewInput(T input);
}

Categories