I was learning Java templates. As generics are compiled, I was hoping runtime deduction of type and hence invocation accordingly.
Seems it doesn't. i.e. deduction is based on compile time type deduction, although template itself is compiled.
Test Interface:
interface op {
public Object Do(Object o);
public Class getClass1();
public Object getTest();
}
Interface implementation
class intop implements op{
public Object Do(Object o) {
return (Integer) o;
}
public Class getClass1() {
return Integer.class;
}
public Object getTest() {
return new Integer(1);
}
}
class strop implements op{
public Object Do(Object o) {
return (String) o;
}
public Class getClass1() {
return String.class;
}
public Object getTest() {
return "Test";
}
}
Template & Specilizations
class Utils {
public static void performOp(Integer i) {
System.out.println("Interger Object :" + i);
}
public static void performOp(String s) {
System.out.println("String object : " + s);
}
public static <T> void performOp(T o) {
System.out.println("Generic Object :" + o.toString());
}
}
Testing
public class GenMethod {
public static void main(String args[]) {
Utils.performOp(1);
Utils.performOp("Test");
Utils u = new Utils();
Integer i = 4;
Object o = i;
ArrayList<op> a = new ArrayList<op>();
a.add(new intop());
a.add(new strop());
Random rn = new Random();
op b = a.get(rn.nextInt(2));
Utils.performOp(b.getClass1().cast(b.getTest()));
Utils.performOp(u);
}
}
I was Expecting output to be something like:
Interger Object :1
String object : Test
Interger Object :1
Generic Object :Utils#64c3c749
But It was :
Interger Object :1
String object : Test
Generic Object :1
Generic Object :Utils#3ce53108
Is there a way to correctly do specialized template method invocation based on runtime type deduction?
PS : I want to avoid the 'instanceof' & 'if..else' ladders as much as possible.
Related
I am writing a java (processing) library for unexperienced students, and am looking for the best architecture for implementing it.
Initialization of an object should be as close as possible to this:
myObject = new General("type1");
Such that myObject will become an instance of Type1 which extends General:
class General {
public General() {}
}
class Type1 extends General {
public Type1() {}
}
class Type2 extends General {
public Type1() {}
}
As far as I know, this isn't possible (choosing between extended classes during initialization), but I'm looking for the closest solution possible.
So far, my best solution is to make a static initializer inside General:
class General {
...
static General init (String type) {
General temp;
if (type.equals("type1") {
temp = new Type1();
}
...
return temp;
}
and the initialization is:
General myObject;
myObject = General.init("type1");
This is far from ideal...
thanks.
you can make a factory class that manages initialization.
instead of doing it inside the parent.
// Empty vocabulary of actual object
public interface IPerson
{
string GetName();
}
public class Villager : IPerson
{
public string GetName()
{
return "Village Person";
}
}
public class CityPerson : IPerson
{
public string GetName()
{
return "City Person";
}
}
public enum PersonType
{
Rural,
Urban
}
/// <summary>
/// Implementation of Factory - Used to create objects.
/// </summary>
public class Factory
{
public IPerson GetPerson(PersonType type)
{
switch (type)
{
case PersonType.Rural:
return new Villager();
case PersonType.Urban:
return new CityPerson();
default:
throw new NotSupportedException();
}
}
}
The State design pattern can be a solution here. Rather than the constructor argument changing the type of the object (which isn't possible) it can set a field of the object, to make it behave as if its type is different.
package stackoverflow.questions;
public class Main {
private interface MyInterface {
String foo();
int bar();
}
private static class Type1 implements MyInterface {
#Override public String foo() { return "lorem ipsum "; }
#Override public int bar() { return 6; }
}
private static class Type2 implements MyInterface {
#Override public String foo() { return "dolor sit amet"; }
#Override public int bar() { return 7; }
}
public static class General {
private final MyInterface type;
public General(String type) {
try {
this.type = (MyInterface) Class
.forName("stackoverflow.questions.Main$" + type)
.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new IllegalArgumentException("Invalid type: " + type);
}
}
public String method1() { return type.foo(); }
public int method2() { return type.bar(); }
}
public static void main(String... args) {
General one = new General("Type1");
General two = new General("Type2");
System.out.println(one.method1() + two.method1());
System.out.println(one.method2() * two.method2());
}
}
I have two classes A and B and they both have a common field in them, and I want to create a function in which if I pass Class A object then I want to set that common field value to the passed value and if I pass Class B object then I want to set that common field value to the passed value. Can anyone please tell me how can I do this, I am new to Java Generic Classes.
Otherwise I would have to make two different functions OR I would have to make an if and else which would decide that passed object belongs to which class ??
Class A
public class A{
int footer;
public void setFooter(int fo) {
footer = fo;
}
}
Class B
public class B{
int footer;
public void setFooter(int fo) {
footer = fo;
}
}
Class D
public class D{
public void change_footer(T generic_param, int value) {
generic_param.setFooter(value);
}
}
Class HelloWorld
public class HelloWorld{
public static void main(String []args){
Here I want to call
A a = new A();
new D().change_footer(a, 5);
B b = new B();
new D().change_footer(b, 5)
}
}
Thank You
And if I got all of the question wrong, and nor A nor B are generic, AND the type of field is fixed.
then you mean something like:
class D {
/*public <T extends Super> would be muuuch nicer here as well!*/
public /*static*/ <T> void change_footer(T obj, int data) {
//otherwise, you could just cast to Super...and set dat field.
if (obj instanceof A) {
((A) obj).setField(data);
} else if (obj instanceof B) {
((B) obj).setField(data);
} // else ... ?
}
}
Original answer:
Easy peasy (the "straight forward" implementation produces the desired results.):
class A<T> {
T daField;
public void setField(T pField) {
daField = pField;
}
public T getField() {
return daField;
}
}
class B<T> extends A {//empty
}
class Test {
public static void main(String... args) {
B<Object> testB1 = new B<>(); //
testB1.setField(new Object());
System.out.println(testB1.getField());
B<String> testB2 = new B<>();
testB2.setField("blah blah");
System.out.println(testB2.getField());
B<Integer> testB3 = new B<>();
testB3.setField(42);
System.out.println(testB3.getField());
}
}
System.out:
java.lang.Object#6d06d69c
blah blah
42
It get's (little) more complicated, when you want to instantiate Ts ...but still possible/other question. :)
Edit to your comment:
If there's only one common field, then why not:
/*abstract */class Super<T> {
T daField;
public void setField(T pField) {
daField = pField;
}
public T getField() {
return daField;
}
}
? ...and:
class A<T> extends Super { ... }
class B<T> extends Super { ... }
Why i cant assign my super class object to base class
class Alpha {
String getType() {
return "alpha";
}
}
class Beta extends Alpha {
String getType() {
return "beta";
}
}
public class Gamma extends Beta {
String getType() {
return "gamma";
}
public static void main(String[] args) {
Gamma g1 = new Alpha();
Gamma g2 = new Beta();
System.out.println(g1.getType() + " "
+ g2.getType());
}
}
Can anyone tell me the reason why it is not possible.
You're trying to assign a child class to a parent class. Which is exactly the opposite of what you should be doing.
Imagine you've got classes:
public class Mammal {
}
public class Dog extends Mammal {
}
public class Cat extends Mammal {
}
Do you think it would be logical to assign:
Cat cat = new Mammal();
Is any Mammal a Cat?
No. A reference to a subclass must refer to an instance of itself or one of its subclasses (or null). Because it will not behave correctly.
What sense does the following program make ?
Object o = new Object();
String s = (string) o;
int i = s.length();
You can add cast to compile code:
package com.stackoverflow.main;
class Alpha {
String getType() {
return "alpha";
}
}
class Beta extends Alpha {
String getType() {
return "beta";
}
}
public class Gamma extends Beta {
String getType() {
return "gamma";
}
public static void main(String[] args) {
Gamma g1 = (Gamma) new Alpha();
Gamma g2 = (Gamma) new Beta();
System.out.println(g1.getType() + " " + g2.getType());
}
}
but it will give you exception at runtime
Exception in thread "main" java.lang.ClassCastException: com.stackoverflow.main.Alpha cannot be cast to com.stackoverflow.main.Gamma
at com.stackoverflow.main.Gamma.main(Gamma.java:21)
Because in java you can't cast instance of base type to subtype
I want to have a method in an interface that returns a class whose type is not defined in the package. The implementing class will then return a specific type. I can see at least 3 methods how I can do this, shown below as fn1, fn2 and fn3. In all cases there is some form of unchecked cast. Is any of these methods preferred? or is there something better? (assume that the interface I1 and the method dostuff are in some other jar package and do not have access to the Test or the Integer class)
public class Myclass {
public interface I1
{
Object fn1();
<T> T fn2();
<T> T fn3();
}
public class Test implements I1
{
#Override
public Integer fn1() {
return new Integer(1);
}
#Override
public <T> T fn2() {
return (T) new Integer(2); //requires cast to T
}
#Override
public Integer fn3() { //automatic unchecked conversion to T in return value
return new Integer(3);
}
}
public static void main(String[] args) {
Myclass c = new Myclass();
I1 t = c.new Test();
Integer i = (Integer) t.fn1(); //cast required here since I1.fn1() returns Object
Integer j = t.fn2();
Integer k = t.fn3();
dostuff(t);
}
static void dostuff(I1 p)
{
Object i = p.fn1();
Object j = p.fn2();
Object k = p.fn3();
}
}
Can't you use generics with the Interface? Like
public interface I1<T> {
T fn1();
// etc
}
Then there's no casting required when you refer to T.
That's what I prefer, at least. You can then also of course specify what you want T to be using
<T extends myInterface>
I would do it this way
public interface I1<T> {
T fn1();
}
public class Test implements I1<Integer> {
#Override
public Integer fn1() {
return new Integer(1);
}
}
public static void main(String[] args) {
Myclass c = new Myclass();
I1<Integer> t = c.new Test();
Integer i = t.fn1(); <-- no cast
}
I'd like to create a generic enum-based mapper for IBatis. I'm doing this with the below code. This does have compile time errors, which I don't know how to fix. Maybe my solution is just plain wrong (keep in mind the use of IBatis), in such case please suggest something better.
Any help appreciated.
What I want to achieve is to define subsequent mappers as:
public class XEnumTypeHandler extends CommonEnumTypeHandler<X> {
}
The current code:
public class CommonEnumTypeHandler<T extends Enum> implements TypeHandlerCallback {
public void setParameter(ParameterSetter ps, Object o) throws SQLException {
if (o.getClass().isAssignableFrom(**T**)) {
ps.setString(((**T**) o).value().toUpperCase());
} else
throw new SQLException("Excpected ParameterType object than: " + o);
}
public Object getResult(ResultGetter rs) throws SQLException {
Object o = valueOf(rs.getString());
if (o == null)
throw new SQLException("Unknown parameter type: " + rs.getString());
return o;
}
public Object valueOf(String s) {
for (T pt : T.**values()**) {
if (pt.**value()**.equalsIgnoreCase(s))
return pt;
}
return null;
}
}
I've added error markings to the above code, the error messages are in order:
T cannot be resolved
The method value() is undefined for
the type T
The method values() is undefined for
the type T
The method values() is undefined for
the type T
I've solved this issue with the following code:
public class CommonEnumTypeHandler<T extends Enum> implements TypeHandlerCallback {
Class<T> clazz;
public CommonEnumTypeHandler(Class<T> clazz) {
this.clazz = clazz;
}
public void setParameter(ParameterSetter ps, Object o) throws SQLException {
if (o.getClass().isAssignableFrom(clazz)) {
ps.setString(((T) o).name().toUpperCase());
} else
throw new SQLException("Excpected " + clazz + " object than: " + o);
}
public Object getResult(ResultGetter rs) throws SQLException {
Object o = valueOf(rs.getString());
if (o == null)
throw new SQLException("Unknown parameter type: " + rs.getString());
return o;
}
public Object valueOf(String s) {
return Enum.valueOf(clazz, s);
}
}
Inheriting from this class I do:
public class SalesChannelTypeHandler extends CommonEnumTypeHandler<SalesChannel> {
public SalesChannelTypeHandler() {
super(SalesChannel.class);
}
public SalesChannelTypeHandler(Class<SalesChannel> clazz) {
super(clazz);
}
}
I'm not sure what you're doing (a general overview in words would be nice), but:
You can't do isAssignableFrom(T) (you need a Class object), and you can't do instanceof T either (generics are non-reified). You may want to pass Class<T> type tokens instead.
Have you looked at EnumMap?
See also
Java Tutorials/Runtime Type Tokens
Neal Gafter's Blog - Super Type Tokens
Josh Bloch - Typesafe Heterogenous Container (THC) pattern (PDF)
It's still not clear what is desired, but perhaps it's something along the lines of this:
enum Color { BLACK, WHITE; }
public static void main(String[] args) {
Color c = Enum.valueOf(Color.class, "black".toUpperCase());
System.out.println(c); // prints "BLACK"
}
So we use Enum.valueOf that takes a type token Class<T extends Enum<T>>, and ask it for the enum constant with a given name. valueOf is NOT case-insensitive, but by conventions, all constants should be in uppercase, so we simply take the query string and turn it .toUpperCase().
As pointed by Polygenelubricants, you need to pass concrete runtime types around, e.g. Class<?> instead of syntactic compiletime types like generic parameters. Here's a rewrite how you could use it:
public abstract class CommonEnumTypeHandler<E extends Enum<E>> implements TypeHandlerCallback {
private Class<E> enumClass;
public CommonEnumTypeHandler(Class<E> enumClass) {
this.enumClass = enumClass;
}
public void setParameter(ParameterSetter ps, Object o) throws SQLException {
if (enumClass.isInstance(o)) {
ps.setString((enumClass.cast(o)).name().toUpperCase());
} else {
throw new SQLException("Excpected ParameterType object than: " + o);
}
}
public Object getResult(ResultGetter rs) throws SQLException {
try {
return Enum.valueOf(enumClass, rs.getString());
} catch (IllegalArgumentException e) {
throw new SQLException("Unknown parameter type: " + rs.getString(), e);
}
}
}
Which you can then use as follows:
public class XEnumTypeHandler extends CommonEnumTypeHandler<X> {
public XEnumTypeHandler() {
super(X.class);
}
}