Okay, I have never used generics heavily and was wondering if I could use them in the following way.
Lets suppose I have this class
public class ASuperClass {
public abstract void doSomething(String arg1, Integer arg2);
}
So, if I then extend the above class I would then have the following, where I would be forced to override doSomething with an argument list of String and Integer.
public class ASubClass extends ASuperClass{
#Override
public void doSomething(String arg1, Integer arg2){
some code.....
}
}
Now, lets suppose that in my subclass I am fine to override doSomething, but I need an additional String arg in my subclass. So, what I would like to do is have the below:
public class ASubClass extends ASuperClass{
#Override
public void doSomething(String arg1, Integer arg2, String arg3){
some code...
}
}
The above won't compile though because my subclass does not implement the abstract signature defined in the base. So, to get it to compile, I can of course do this:
public class ASubClass extends ASuperClass{
#Override
public void doSomething(String arg1, Integer arg2){}
public void doSomething(String arg1, Integer areg2, String arg3){
here is my code......
}
}
So, what exactly am I trying to do you are probably asking? What I am interested in is, is there a way to "force" any subclass of a class, using generics as the arg, to implement a base abstract method regardless of arg list. So that the following would compile extending the ASuperClass:
public class ASubClass extends ASuperClass{
#Override
public void doSomething(String arg1, Integer areg2, String arg3){
here is my code......
}
}
public class AnotherSubClass extends ASuperClass{
#Override
public void doSomething(String arg1, Integer areg2, String arg3, Integer arg4){
here is my code......
}
}
So, can you "generic-ize" the base class arg list so the above two classes would compile? Something like below? I know the syntax below is wrong, but can you make the arg list generic?
public class ASuperClass {
public abstract void doSomething(<?>);
}
Overall, my idea is to enforce consistency in an application where folks are extending some base functionality. Basically make sure that every sublass of ASuperClass has a doSomething() member function, to ensure consistency (method naming in particular) across subclasses, but it's arg list may be different per subclass.
Hopefully the above is not too confusing. Interested to know if this is possible.
Method parameters are a part of the method's "signature", which is used to decide overriding. In order for a subclass to override a method in its base class, to implement an abstract method of its abstract base class, or to implement an interface method, the entire signature, including the parameter types, must match.
If you would like the argument list to take a variable number of parameters, you can use this syntax:
public abstract void doSomething(Object ... args);
You can override a method with this signature in subclasses, and you can pass as many arguments as you wish. Some unfortunate consequence of this design decision are that
Your parameters are now untyped - each method needs to validate its list of parameters, including their type, before proceeding with the calculations, and
Parameters of primitive types need to be wrapped in Objects - primitives need to be boxed, or autoboxed.
public class ASuperClass
{
public abstract void doSomething(Object ... args);
}
public class ASubClass extends ASuperClass
{
#Override
public void doSomething(Object ... args)
{
here is my code......
}
}
This will allow both calls: doSomething(str1, int1) and doSomething(str1, int1, str2, int2);
If you define an abstract method like this it means that any object of a type that implements this abstract class is guaranteed to include this method.
For instance look at this code example.
List<ASuperClass> list = new ArrayList<ASuperClass>();
list.add(new ASubClass());
list.add(new AnotherSubClass());
So we create a list of objects all of type ASuperClass and then add two objects to this list. These objects are of different types but they both share this common parent. This means we can guarantee that they will respond to all of the method signatures on the ASuperClass type.
for(ASuperClass obj : list)
{
obj.doSomething("parm1", 1);
}
If you let the subtype determine the parameter list then this would break this. We would know that there was a method called doSomething but it would be useless to us because there would be no way of knowing what the parameters should be.
As others have suggested there is a hacky way of doing this.
public abstract void doSomething(Object ... params);
Whilst this is strictly true I'm sure nobody in their right mind would suggest actually doing this. Its only been suggested for completeness.
If on the other hand you wanted a variable number of parameters of the same type then you could use it more safely.
public abstract void doSomething(String parm1, Integer parm2, String ... params);
In this case you will always require a String, an Integer and then a variable number of strings. It's really equivalent to doing this.
public abstract void doSomething(String parm1, Integer parm2, String[] params);
Related
I am trying to make a method which takes in a variable object type as a parameter, and then updates a database depending on what object was passed, a bit like how Petapoco works for C#. For this I am trying to use a generic method in which the object that is passed passes a number of checks before it is inserted/updated into its relevant table. A number of questions on Stack Overflow point me towards using a method signature like this:
public <T> void upsertObject(Class<T> objectClass, T actualObject) { }
Within the method I would like to be able to call methods like actualObject.getId() but the compiler needs to know the actual type of the object to call its relevant methods. Is there any way around this? How would I go about achieving these calls?
EDIT:
Edited for more clarity
After adding an interface with which to bind T, I found I was getting the error:
"Cannot make a static reference to the non-static method getId() from the type CycleComponent"
The method now looks like this, the error line is under T.getId()
public <T extends CycleComponent> void upsertObject(Class<T> objectClass, T fitProObject) {
if (objectClass.getName() == "Cycle") {
if (isInserted(T.getId(), "Cycles")) {
// update the database
}else {
// insert into the database
}
}
}
The interface looks like:
public interface CycleComponent {
String getId();
}
And the method in the Cycle class looks like:
public String getId() {
return this.cycleId;
}
You can do it with generic type bounds.
Define:
public <T extends SomeType> void upsertObject(Class<T> objectClass, T actualObject) { }
Where SomeType is a class or interface, and you'll be able to call methods of SomeType on the actualObject instance.
This would limit this method to being used only with types that extend SomeType (if it's a class) or implement SomeType (if it's an interface).
For example:
public interface SomeType {
String getId();
}
And:
public <T extends SomeType> void upsertObject(Class<T> objectClass, T actualObject) {
if (actualObject.getId() != null) {
...
}
}
Assuming this doesn't have to be absolutely generic (for example, it probably doesn't make sense to upsert a java.lang.Integer, right?), I'd define an interface with all the methods you need, and add that to the generic classification:
public interface Upsertable {
int getID();
// Other methods you may need...
}
public <T extends Upsertable> void upsertObject(Class<T> objectClass, T actualObject) { }
I was very surprised when I noticed that following code compiles without warnings and prints Integer / String:
public final class GenericsTest {
private static <T> void method(T arg1, T arg2) {
System.out.println(arg1.getClass().getSimpleName());
System.out.println(arg2.getClass().getSimpleName());
}
public static void main(String[] args) {
method(1, "1");
}
}
I expected a compilation error.
Is there a reason why this code compiles?
What is the correct way to ensure that arguments have the same type?
Edit: What about bounded type parameters? The best I can think of is this:
private static <T, U extends T> void method(T arg1, U arg2) {
System.out.println(arg1.getClass().getSimpleName());
System.out.println(arg2.getClass().getSimpleName());
}
Unfortunately, java doesn't allow cyclic constraints. <T extends U, U extends T> doesn't compile. Is this a dead end?
The reason that this compiles is because Java will infer the most specific supertype of the arguments passed in, in this case, Object Serializable & Comparable<? extends Serializable & Comparable<? extends Comparable<?>>>, after 1 is boxed to Integer and "1" is passed as a String.
Without generics:
private static void method(Number arg1, Number arg2) {
Even without generics, you can pass in an Integer and a Double.
Only if the type in question is final can you do this, without generics:
private static void method(String arg1, String arg2) {
// Yes, they're both Strings, guaranteed.
There is one edge case with generics that I can think of to ensure that they are the exact type. If you have a final class, and you place an upper bound, then you can restrict it to that same class.
public <T extends MyFinalClass> void method(T arg1, T arg2) {
// Yes, they're both MyFinalClasses
}
But then you could do the same thing without generics.
public void method(MyFinalClass arg1, MyFinalClass arg2) {
// Yes, they're both MyFinalClasses
}
You could add the class as an additional parameter.
private static <T> void method(T arg1, T arg2, Class<T> type) {
// ...
}
Now you have to specify the common type.
You can still call method(1, "1", Object.class); but at least you are explicit about the common type.
It is not possible to do this. Or to look at it another way, two reference arguments are always "the same type" -- Object -- any arguments of reference type are always instances of Object.
T can always be Object, and take any two reference arguments. Even with <T, U extends T> void method(T arg1, U arg2), both T and U can be Object, and thus take any two arguments again.
The underlying reason for this is that there is no type-safety reason to have such a constraint. One of the major points of inheritance is that it should be possible to treat subclass instances like a superclass instance safely. A reference type of superclass type can point to an instance of that class or subclass freely. Therefore, two reference variables that have the same compile-time type, can always point at runtime to instances of different subclass types, completely safely. So you can never, at compile time, make any statement about the relationship of the actual runtime classes of two instances, other than they are subclasses of the compile-time type. Since it's safe for the two arguments to be instances of different classes at runtime, it can be no less safe to actually pass two arguments of different compile-time types.
This is how I solved this problem - I wanted to create a method that creates a diff of two objects.
public List<Change> getChanges(Object o1, Object o2);
Naturally I wanted both o1 and o2 to be of the same type. I solved this by encapsulating this method inside a parameterised class.
public class DiffGenerator<T> {
public List<Change> getChanges(T o1, T o2) {
//code
}
}
This can be used as:
List<Change> changes = new DiffGenerator<MyClass>().getChanges(myClassOld, myClassNew);
Worked for me.
Say I'm making a class that implements an interface, and have code like this:
public void setGoalLocation(Location loc)
{
goal = loc;
}
The code doesn't compile, because it demands that I implement a "setGoalLocation(Ilocation loc)" method, where "Ilocation" is an interface and "Location" is an actual concrete class that implements it.
This means that I have to do something like this:
public void setGoalLocation(ILocation loc)
{
goal = (Location)loc;
}
That just seems really awkward. And funnily enough, Java doesn't seem to care about other methods returning Location instead of the interface ILocation. This works:
public Location getStartLocation()
{
return start;
}
...even though the "required" method would be a "public ILocation getStartLocation". Can anyone explain why this is, and any help for making the code less awkward? I'd like to be able to use a Location as a parameter, not an ILocation.
The problem is that the interface requires a method that accepts anything as an argument that is a subtype of ILocation, not just an object of the specific type Location. If you had another concrete type Position that was a subtype of ILocation, then implementing the interface would require you to accept a Position object as well as a Location object.
Note that in your work-around using a cast, you'd get a ClassCastException at run time if you happened to pass a Position instead of a Location object.
As a design issue, to get around this you could define your interface as a generic:
interface <T extends ILocation> TheInterface {
void setGoalLocation(T loc);
}
Then your concrete class can bound the generic parameter:
public class MyClass implements TheInterface<Location> {
public void setGoalLocation(Location loc) {
. . .
}
}
As to return types, that works because any Location object is an ILocation, so when you return a Location you are returning an ILocation.
Java supports covariant return types where the return type of a method in a subclass (or interface implementation) can return a subclass (or implementation) of the declared type. So in general, the following is allowed
public class A {}
public class B extends A {}
public class C {
A getSomething();
}
public class D extends C {
B getSomething();
}
If the interface has a method that takes an interface type, you cannot override it with a different signature.
public interface I {
void setSomething(ISomething somethingInterface);
}
You cannot do
public class Something implements ISomething {}
public class MyI implements I {
void setSomething(Something somethingInterface);
}
I am trying to create following enum.
public enum MyEnum{
LEAD {
#Override
public boolean isValid(Lead lead) { //compile error, asks to retain type as T
}
},
TASK {
#Override
public boolean isValid(Task task) { //compile error, asks to retain type as T
}
};
public abstract <T extends SObject> boolean isValid(T object);
}
Lead and Task classes both extend SObject. My intention is to basically let clients be able to use MyEnum.LEAD.isValid(lead) or MyEnum.TASK.isValid(task). Compiler shouldn't allow to pass other types.
Could someone help in understand why this is happening.
Thanks
You need to override the generic method with the same generic method. If you want to do what you are asking you need a generic class - which an enum cannot be.
The point being that I refer to the enum by the class reference - i.e.
final MyEnum myenum = MyEnum.LEAD;
Now if I call myenum.isValid() I should be able to call it with any SObject as defined by your abstract method.
The generic method definition that you have doesn't actually do anything. All it is doing is capturing the type of the passed in SObject and storing it as T. A generic method is commonly used to tie together types of parameters, for example
<T> void compare(Collection<T> coll, Comparator<T> comparator);
Here we do not care what T actually is - all we require is that the Comparator can compare the things that are in the Collection.
What you are thinking of is a generic class, something like:
interface MyIface<T> {
boolean isValid(T object);
}
And then
class LeadValid implements MyIface<Lead> {
public boolean isValid(Lead object){}
}
You see the difference is that you would have a MyIface<Lead> - you would have to declare the type of MyIface. In the enum case you only have a MyEnum.
I have an abstract class that has a generic method and I want to override the generic method by substituting specific types for the generic parameter. So in pseudo-code I have the following:
public abstract class GetAndParse {
public SomeClass var;
public abstract <T extends AnotherClass> void getAndParse(T... args);
}
public class Implementor extends GetAndParse {
// some field declarations
// some method declarations
#Override
public <SpecificClass> void getAndParse(SpecificClass... args) {
// method body making use of args
}
}
But for some reason I'm not allowed to do this? Am I making some kind of syntax error or is this kind of inheritance and overriding not allowed? Specifically I'm getting an error about #Override because the eclipse IDE keeps reminding me to implement getAndParse.
Here's how I want the above code to work. Somewhere else in my code there is a method that expects instances of objects that implement GetAndParse which specifically means that they have a getAndParse method that I can use. When I call getAndParse on that instance the compiler checks to see whether I have used specific instances of T in the proper way, so in particular T should extend AnotherClass and it should be SpecificClass.
What we are having here is two different methods with individual type parameters each.
public abstract <T extends AnotherClass> void getAndParse(Args... args);
This is a method with a type parameter named T, and bounded by AnotherClass, meaning each subtype of AnotherClass is allowed as a type parameter.
public <SpecificClass> void getAndParse(Args... args)
This is a method with a type parameter named SpecificClass, bounded by Object (meaning each type is allowed as a type parameter). Do you really want this?
Is the type parameter used inside Args? I think the problem would be there.
The meaning of
public abstract <T extends AnotherClass> void getAndParse(T... args);
is that the caller of the method can decide with which type parameter he wants to call the method, as long as this is some subtype of AnotherClass. This means that in effect the method can be called with any objects of type AnotherClass.
Since the caller can decide the type parameter, you can't in a subclass narrow down the parameter type to SpecificClass - this would not be an implementation of the method, but another method with same name (overloading).
Maybe you want something like this:
public abstract class GetAndParse<T extends AnotherClass> {
public SomeClass var;
public abstract void getAndParse(T... args);
}
public class Implementor extends GetAndParse<SpecificClass> {
// some field declarations
// some method declarations
#Override
public void getAndParse(SpecificClass... args) {
// method body making use of args
}
}
Now the getAndParse method implements the parent class' method.
You are seeing this problem because of the concept called "Erasure" in Java Generics.
Java uses "erasure" to support backward compatibility. i.e Java code which did not use generics.
Erasure Procedure:
The compiler will first do a type checking and then it will remove(erase) all the type parameters as much as possible, and also insert TypeCasting where ever necessary.
example:
public abstract <T extends AnotherClass> void getAndParse(T paramAnotherClass);
will become
public abstract void getAndParse(AnotherClass paramAnotherClass);
In class "Implementor.java",
The code
public <SpecificClass> void getAndParse(T paramAnotherClass)
will become
public void getAndParse(SpecificClass paramAnotherClass){ }
the compiler will see that you have not implemented the abstract method correctly.
There is a type mismatch between the abstract method and the implemented method. This is why you are seeing the error.
More details can be found here.
http://today.java.net/pub/a/today/2003/12/02/explorations.html
You cannot override to specific type T because there is in fact (at the bytecode level if you wish) only one method getAndParse because of type erasure (see other answer):
public abstract void getAndParse(AnotherClass... args); // (1)
For every type of T, the same method is used.
You can overload it (I think):
public void getAndParse(SpecificClass... args); // (2)
but this will not a different method from (1) ant it will not be called by generic code:
T x = whatever;
object.getAndParse(x); // Calls (1) even if T is derived from SpecificClass
No, it's not valid. What would happen if someone with a GetAndParse reference called it with a different class extending AnotherClass?
That becomes a nonsense when someone has a reference to type GetAndParse and tries to call the getAndParse method. If Cat and Dog extend AnotherClass. I should expect to be able to call GetAndParse#getAndParse with either a Cat or a Dog. But the implementation has tried to restrict it and make it less compatible!
Static method can't override
class Vehicle{
static void park(int location){
System.out.println("Vehicle parking..");
}}
class Car extends Vehicle{
#Override //error
void park(int location) { //error
System.out.println("Car Parking..");
}}
Private method can't override
class Vehicle{
private void park(int location){
System.out.println("Vehicle parking..");
}
void callPark(){
park(100);
}}
class Car extends Vehicle{
//#Override
void park(int location) {
System.out.println("Car Parking..");
}}
class Demo {
public static void main(String[] args) {
Vehicle v1=new Car();
v1.callPark();
}}
Final method can't override
class Vehicle{
final void park(int location){
System.out.println("Vehicle parking..");
}}
class Car extends Vehicle{
//#Override
void park(int location) { //error
System.out.println("Car Parking..");
}}