This question already has answers here:
Overloaded method selection based on the parameter's real type
(7 answers)
Closed 10 years ago.
I was going through the method overloading in java and I was trying the output of the below program in eclipse , the program is ..
public class OverloadingTest {
public static void main(String args[]){
List abc = new ArrayList();
List bcd = new LinkedList();
ConfusingOverloading co = new ConfusingOverloading();
co.hasDuplicates(abc); //should call to ArryList overloaded method
co.hasDuplicates(bcd); //should call to LinkedList overloaded method
}
}
class ConfusingOverloading{
public boolean hasDuplicates (List collection){
System.out.println("overloaded method with Type List ");
return true;
}
public boolean hasDuplicates (ArrayList collection){
System.out.println("overloaded method with Type ArrayList ");
return true;
}
public boolean hasDuplicates (LinkedList collection){
System.out.println("overloaded method with Type LinkedList ");
return true;
}
}
and the output is ..
Output
overloaded method with Type List
overloaded method with Type List
Now in the explanation it was told ..method overloading is resolved at compile time using static binding in Java, so please advise how can I achieve the same through method overriding.
abc,bcd both are of type List even when you initialize it with a subclass.hence the result
A BaseClass like List helps you to write methods that can work with any of it's subclass(ArrayList or LinkedList).
So,
public ArrayListLinkedListCanCallMe(List lst)
{
//now imagine if this method was called with bcd as parameter
//still lst would be of type List not LinkedList
//and if lst were allowed to be of type LinkedList then how could List know any
//of the methods of LinkedList.Therefore lst would always be of type List NOT LinkedList
}
You can instead try
co.hasDuplicates((ArrayList)abc);
co.hasDuplicates((LinkedList)bcd);
But (ArrayList)abc can throw cast exception if abc is of type LinkedList.You can use instanceof operator to check if abc is of type ArrayList and then cast it..
In this particular case, if hasDuplicates means what it says, I would have one method taking List as argument. It would just create a HashSet initialized with the List contents and compare its size to the List size.
If you really do need special case code for e.g. ArrayList you could use instanceof inside a List argument method.
However, when you are working with an interface type it is much, much better to find common approaches that will work for all implementations of the interface. If you cannot do that, you must allow for the possibility of being passed an object of a class that implements the interface but is not one of the classes for which you have special case code. If you need special case code for java.util.ArrayList, why don't you need special case code for instances of the private class that Arrays.asList uses for its result?
When the issue is different classes in your own code, you can often turn the problem around and put the method in what is currently the argument class, so that conventional overriding works.
Related
So I'm taking a Java class, and one of the assignments we were given involves abstract data types (ADTs). In this assignment, we're supposed to implement an ADT known as Stack through a class called LinkedStack. LinkedStack has one constructor, but in the test cases my professor provided us, a new LinkedStack object can create either a new LinkedList object or a new ArrayList object. My issue with this is that no matter how I define my argument, I still get an error saying that the argument is incompatible with the classes.
I've tried a logical test to see whether the argument was called as a LinkedList or an ArrayList, which I think is a good thing, but I can't figure out how to properly assign the argument.
I've tried setting the argument to a Stack and then casting to a LinkedStack, with a private final variable being of of type "Stack", I've also tried calling the argument as a List, but I can't seem to get rid of the errors preventing me from starting the compiling process.
This is what we start out with:
interface Stack {
public void push(Object d){
}
public Object pop(){
}
public Object peek(){
}
public boolean isEmpty(){
}
}
public class ListStack implements Stack{
public ListStack(/*argument*/){
}
}
//Separate test case file
//example of the test cases
public void peekTest1() {
Stack q = new ListStack(new LinkedList());
// assertion cases follow
}
public void peekTest2() {
Stack q = new ListStack(new ArrayList());
// assertion cases follow
}
If you look for a type that you can use for /*argument*/, you can do it like this:
public class ListStack implements Stack {
public ListStack(List list) {
}
/* note that you must implement all methods from the interface */
}
Why use type List? List is the common interface, implemented by LinkedList and ArrayList. So you can use one of them in the constructor.
Note: You should not use raw types. List and the classes that implement this interface have a type parameter. When possible you should rather use something like List<String> or List<T>. But maybe, you will learn this in a later lesson.
Why does java allows inconsistent type to be entered into a generic object reference but not in an array?
For Eg:
When initializing array:
int[] a = {1, 2, 3};
And, if I enter:
int[] a = {1, 2, "3"}; //Error for incompatible types
While for generics,
import java.util.ArrayList;
public class Test {
private static ArrayList tricky(ArrayList list) {
list.add(12345);
return list;
}
public static void main(String[] args) {
int i = 0;
ArrayList<String> list = new ArrayList<>();
list.add("String is King");
Test.tricky(list);
}
}
The above code will let you add any Type in the list object, resulting in a run time exception in some cases.
Why is there such a behavior?? Kindly give a proper explanation.
When you use the tricky method to insert data into your ArrayList Collection, it doesn't match the specified type i.e String, but still This is compatible because of Generics compatibility with older Legacy codes.
If it wouldn't have been for this i.e if it would have been the same way as of arrays, then all of the pre-java generic code would have been broken and all the codes would have to be re-written.
Remember one thing for generics, All your type-specifications are compile time restrictions, so when you use the tricky method to insert data in your list reference, what happens is the compiler thinks of it as a list to which ANYTHING apart from primitives can be added.
Only if you would have written this:
...
public class Test {
private static ArrayList tricky(ArrayList<String> list) {
list.add(12345); //Error, couldn't add Integer to String
return list;
}
...
}
I have written a documented post on this, Read here.
The method's parameter has no generic so all classes are allowed.
You may google 'type erasure' for more information.
If you add the generic type to your method you will get a compiler error:
private static ArrayList<String> tricky(ArrayList<String> list) { // ...
By the way, you do not need to return the list because you modify the same instance.
Here's why:
The reason you can get away with compiling this for arrays is because
there is a runtime exception (ArrayStoreException) that will prevent
you from putting the wrong type of object into an array. If you send a
Dog array into the method that takes an Animal array, and you add only
Dogs (including Dog subtypes, of course) into the array now referenced
by Animal, no problem. But if you DO try to add a Cat to the object
that is actually a Dog array, you'll get the exception. Generic
Methods (Exam Objectives 6.3 and 6.4) 615 616 Chapter 7: Generics and
Collections
But there IS no equivalent exception for generics, because
of type erasure! In other words, at runtime the JVM KNOWS the type of
arrays, but does NOT know the type of a collection. All the generic
type information is removed during compilation, so by the time it gets
to the JVM, there is simply no way to recognize the disaster of
putting a Cat into an ArrayList and vice versa (and it becomes
exactly like the problems you have when you use legacy, non-type safe
code)
Courtesy : SCJP Study guide by Kathy Sierra and Bert Bates
When you declare you ArrayList like ArrayList list = ... you do not declare the type of object your list will contain. By default, since every type has Object as superclass, it is an ArrayList<Object>.
For good practices, you should declare the type of your ArrayList<SomeType> and, thereby, avoid adding inconsistant elements (according to the type)
Because you haven't defined the generic type of your list it defaults to List<Object> which accepts anything that extends Object.
Thanks to auto-boxing a primitive int is converted to an Integer, which extends Object, when it is added to your list.
Your array only allows int's, so String's are not allowed.
This is because in your method parameter you did not specify a particular type for ArrayList so by default it can accept all type of objects.
import java.util.ArrayList;
public class Test {
//Specify which type of objects you want to store in Arraylist
private static ArrayList tricky(ArrayList<String> list) {
list.add(12345); //This will give compile time error now
return list;
}
public static void main(String[] args) {
int i = 0;
ArrayList<String> list = new ArrayList();
list.add("String is King");
Test.tricky(list);
}
}
I am creating a class that, at present, stores lists of various types in an internal object called genericTable. Each list (composed of either Double, or Long) are all held in an object which is an instance of class GenericList.
Question: Why doesn't the method addVector work?
The error under the red underline says the constructor Test<V>.GenericList<V>(List<List<V>>) is undefined.
If I was working in a main method (but had the same GenericList class) and created genericTable within the main method (using List<GenericList<?>> Table = new ArrayList<GenericList<?>>();) and did genericTable.add(new GenericList<Long>(Arrays.asList(genericVector))); (where genericVector in this case is a List<Long>), it works perfectly.
public class Test<V> {
private final List<GenericList<?>> genericTable = new ArrayList<GenericList<?>>();
public void addVector(List<V> genericVector) {
genericTable.add(new GenericList<V>(Arrays.asList(genericVector)));
}
private class GenericList<K> {
private final List<K> listGeneric;
public GenericList(List<K> input) {
listGeneric = input;
}
}
}
You're unnecessarily using Arrays.asList(), when you already have a list. Consequently you get a list of lists, which is not what the constructr accepts.
See this from the javadocs:
This method also provides a convenient way to create a fixed-size list
initialized to contain several elements:
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
So in your case you're getting a list of lists, instead of a list of strings.
I've added this bit from the comments, for clarity:
The method signature for asList() is like this:-
public static <T> List<T> asList(T... a)
So because T... a is a vararg, when you pass in "Larry", "Moe", "Curly", the compiled method actually receives an array of ["Larry", "Moe", "Curly"], and returns them as a List.
So because you passed in a List, rather than an array, the method takes the vararg array like this: [genericVector], and returns that array as a list, and you constructor breaks.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Create instance of generic type in Java?
Java how to: Generic Array creation
I am trying to create a create a class of generic type. This is my class file.
public class TestClass<T> implements AbstractDataType<T>{
T[] contents;
public TestClass(int length) {
this.contents = (T[])new Object[length];
}
}
But the contents have just have the methods inherited from the Object class. How can I create an abstract array for contents ?
As far as initializing contents, I think what you have is the best you can do. If there way a way, ArrayList would probably do it (line 132: http://www.docjar.com/html/api/java/util/ArrayList.java.html)
But when you say "the contents have just have the methods inherited from the Object class", I'm assuming you mean that you can only access methods like toString and equals when you are working with a T instance in your code, and I'm guessing this is the primary problem. That's because you're not telling the compiler anything about what a T instance is. If you want to access methods from a particular interface or type, you need to put a type constraint on T.
Here's an example:
interface Foo {
int getSomething();
void setSomethingElse(String somethingElse);
}
public class TestClass<T extends Foo> implements AbstractDataType<T> {
T[] contents;
public TestClass(int length) {
this.contents = (T[])new Object[length];
}
public void doSomethingInteresting(int index, String str) {
T obj = contents[index];
System.out.println(obj.getSomething());
obj.setSomethingElse(str);
}
}
So now you can access methods other than those inherited from Object.
You cannot create a generic array in Java.
As stated in the Java Language Specification, the mentioned rules state that "The rules above imply that the element type in an array creation expression cannot be a parameterized type, other than an unbounded wildcard."
I believe that in any method that accesses contents, you need to cast them as type T. The main reasoning for this is that as an Object array, Java looks at the contents as Objects to fit them in. So while contents might be an array of T, it still is just an array of type Object.
How do you think ArrayList.toArray and Arrays.copyOf do it?
See Array.newInstance.
public TestClass(Class<T> type, int length) {
this.contents = Array.newInstance(type, length);
}
I am creating several functions in which I want to return the interface instead of the implementation, I.E. List instead of ArrayList . My method signature and a brief implementation follows:
public List<MyAwesomeComposedObject> convert(Collection<MyAwesomeObject> awesomeObjects>)
{
List<MyAwesomeComposedObject> composedObjects = new ArrayList<MyAwesomeComposedObject>();
for(MyAwesomeObject awesomeObject : awesomeObjects)
{
MyAwesomeComposedObject composedObject = new MyAwesomeComposedObject(awesomeObject);
composedObjects.add(composedObject);
}
List<MyAwesomeComposedObject> composedObjectList = Collections.checkedList<composedObjects, MyAwesomeComposedObject.class);
return composedObjectList;
}
My question is, is this an antipattern of some sort? I want to guarantee that the invoker of this method is getting the interface instead of an implementation. I also do not believe this to be a case of overengineering. If this is not the correct way to return an interface, in this scenario I am open to the correct implementation.
Attached is a small program that results in an exception:
public static void main(String[] args)
{
Vector v = (Vector) c();
}
static List<Object> c()
{
List<Object> l = new ArrayList<Object>();
l.add(new Object());
List<Object> o = Collections.checkedList(l, Object.class);
return o;
}
The javadoc is here: checked list
The List returned is a Collections.CheckedList not a Vector. You cannot the reference to a type the object is not.
However what you can do is
public static void main(String[] args) {
Vector<Object> v = new Vector<Object>(c());
}
composedObjects is already a List, you can return that.
public List<MyAwesomeComposedObject> convert(Collection<MyAwesomeObject> awesomeObjects>) {
List<MyAwesomeComposedObject> composedObjects = new ArrayList<MyAwesomeComposedObject>();
for(MyAwesomeObject awesomeObject : awesomeObjects)
composedObjects.add(new MyAwesomeComposedObject(awesomeObject));
return composedObjects;
}
For your revised question: There is no way to prevent the caller from attempting to cast to whatever they want. If it is an inappropriate cast they will get the exception. This is the very reason why casting from an interface to a concrete class is strongly discouraged.
If you are really worried about this, consider returning an ArrayList instead of a List. That should discourage casting since they are getting a concrete type. Please note that I do not endorse this, it is just an option.
I want to guarantee that the invoker of this method is getting the interface instead of an implementation
This is not valid. You are returning a List where the declared type of the elements is an interface, however each element must be SOME instantiation. All a checked collection does is prevent the addition of elements of the incorrect type. There is nothing that prevents the user from casting back to the implementation type.
If you are attempting to ensure that the user gets List instead of ArrayList (my assumption here because I don't see an interface for you Awesome class), this again is flawed because the user could still cast the List to an ArrayList although this would be a bad idea since it risks a ClassCastException.
No, I recommend to keep the code as simple as possible. Read the Javadoc for a discussion when to use Collections.checkedList
http://download.oracle.com/javase/7/docs/api/java/util/Collections.html#checkedCollection%28java.util.Collection,%20java.lang.Class%29