How these strange override rules with erasure can be explained? - java

What is happening here and how to understand it?
class C<T> {
public T id(T x) {
return null;
}
}
class D extends C<String> {
public Integer id(Integer x) { // compiles! but T shall be String ???
return 7;
}
}
public static void main(String[] args) {
System.out.println(new D().id("7")); // null
System.out.println(new D().id(7)); // 7
}
By the way if I declare D like this, compilation fails with Name clash: The method id(Object) of type D has the same erasure as id(T) of type C<T> but does not override it:
class D extends C<String> {
public Object id(Object x) { // compile error !
return 7;
}
}

The compiler replaces your type parameters with Object after it's checked that they've been used correctly. So after compilation, your original class D has
public Object id(Object x)
and
public Integer id(Integer x)
which is perfectly fine.
However, in your second example, there are two versions of public Object id(Object x) in the same class.

Use #Override annotation to check if the method really overrides another method.
In the first case you will have 2 methods, one that expect String and another that expect Integer.
In the second case you created the same method that already exits after type erasure of supertype.

Related

Why can't overriding methods specify a type parameter if the overriden method doesn't?

The following code does not compile, however, if I change f(object) to f(String) or f(Integer) it compiles.
I've read the other posts about the subject, but I still don't get it, how come the compiler doesn't know which method to use
(in case of a new instance A a = new B();)
public class SampleTester {
public static class A {
public void f(int x) { System.out.print("1"); }
public void f(Object x) { System.out.print("2"); }
}
public static class B extends A {
public <T> void f(T x) { System.out.print("3"); } //compiler error
}
public static void main(String[] args) {
A a = new A();
B b= new B();
a.f(3);
a.f("foo");
b.f(3);
b.f("foo");
}
}
If I change T x to Object t it still doesn't compile, so what's the difference? and besides, why it doesn't just override the function from A? (both has the same signature after type erasure
The class B extends A and thus inherits all the methods that are present in A. This means that B will have 3 methods :
public void f(int x)
public void f(Object x)
public <T> void f(T x)
The problem is that f(T x) will go through type erasure during compilation and T will be replaced with Object, thus resulting in a duplicate method since you already have a method that takes an Object argument.
To fix this, either remove the method that takes an Object argument or provide an upper bound for T. (example T extends Number so that T is replaced with Number during type-erasure)
To address your comment (which I have now added to your question) :
public <T> void f(Object x) doesn't compile because it is not a valid override for public void f(Object x) from A.
The method public <T> void f(Object x) belongs to the subclass and can be called using a superlcass reference as a.<Integer>f(null); or a.<String>f(null);. Since overriden methods are resolved at runtime BUT generics go through type erasure at compile time itself, there is no way for the compiler to know whether to substitute T with Integer or String at compile time.
It is still legal to reverse the methods such that you have the method public <T> void f(Object x) in A and the method public void f(Object x) in B since the compiler has all the information necessary to decide what <T> should be replaced with.

Declare a method to return this (java)

It is easy to define a method returning the same value as the argument:
class A {
public static void main(String[]args) {
Derived sth = new Derived();
String x = sth.foo("hello");
System.out.println(x);
Derived resultTypeIsKnown = sth.foo(sth); // <==== !!!
System.out.println(""+resultTypeIsKnown);
}
}
class Base {
<T>T foo(T t)
{
return t;
}
}
class Derived extends Base {
}
You see that the compiler knows that although foo() is declared in the Base class, the compiler knows that sth.foo(sth) returns an instance of the Derived class:
Derived derivedRatherThanBase = sth.foo(sth);
How do I declare that the return value is of the same class as the object whose method is called? (In particular, for a method that always returns this?)
Something like:
class Base {
<?ThisClass?> bar() {
return this;
}
}
I can write
<T>T getThis(T t)
{
return (T)this;
}
but x.getThis(x) uses an extra argument and produces a warning.
UPDATE OMG, what they are doing in the "possible duplicate"... But I already have the base class, it's a descendant of Collection. In other words, it is required that class Base extends Collection and knows its class.
And the real life code where I want to know THISCLASS already is very complex.
UPDATE2 As to the XY problem, what I want is:
class MyVerySpecialCollectionBase extends ... {
...
THISCLASS addAll(Collection items) { ... }
}
class MyExtendedVerySpecialCollection extends MyVerySpecialCollectionBase {
...
}
// use:
MyExtendedVerySpecialCollection x =
new MyExtendedVerySpecialCollection(aLotOfArgs)
.addAll(list1)
.addAll(list2);
The proposed solution sounds too complex and even less acceptable than either (1) redefining addAll() in each derived class or (2) making x.addAll(list1).addAll(list2); a separate statement.

What is name this expression? [duplicate]

I was exploring the Java 8 source and found this particular part of code very surprising:
// Defined in IntPipeline.java
#Override
public final OptionalInt reduce(IntBinaryOperator op) {
return evaluate(ReduceOps.makeInt(op));
}
#Override
public final OptionalInt max() {
return reduce(Math::max); // This is the gotcha line
}
// Defined in Math.java
public static int max(int a, int b) {
return (a >= b) ? a : b;
}
Is Math::max something like a method pointer? How does a normal static method get converted to IntBinaryOperator?
Usually, one would call the reduce method using Math.max(int, int) as follows:
reduce(new IntBinaryOperator() {
int applyAsInt(int left, int right) {
return Math.max(left, right);
}
});
That requires a lot of syntax for just calling Math.max. That's where lambda expressions come into play. Since Java 8 it is allowed to do the same thing in a much shorter way:
reduce((int left, int right) -> Math.max(left, right));
How does this work? The java compiler "detects", that you want to implement a method that accepts two ints and returns one int. This is equivalent to the formal parameters of the one and only method of interface IntBinaryOperator (the parameter of method reduce you want to call). So the compiler does the rest for you - it just assumes you want to implement IntBinaryOperator.
But as Math.max(int, int) itself fulfills the formal requirements of IntBinaryOperator, it can be used directly. Because Java 7 does not have any syntax that allows a method itself to be passed as an argument (you can only pass method results, but never method references), the :: syntax was introduced in Java 8 to reference methods:
reduce(Math::max);
Note that this will be interpreted by the compiler, not by the JVM at runtime! Although it produces different bytecodes for all three code snippets, they are semantically equal, so the last two can be considered to be short (and probably more efficient) versions of the IntBinaryOperator implementation above!
(See also Translation of Lambda Expressions)
:: is called a method reference. It is basically a reference to a single method. I.e., it refers to an existing method by name.
Short Explanation:
Below is an example of a reference to a static method:
class Hey {
public static double square(double num){
return Math.pow(num, 2);
}
}
Function<Double, Double> square = Hey::square;
double ans = square.apply(23d);
square can be passed around just like object references and triggered when needed. In fact, it can be just as easily used as a reference to "normal" methods of objects as static ones. For example:
class Hey {
public double square(double num) {
return Math.pow(num, 2);
}
}
Hey hey = new Hey();
Function<Double, Double> square = hey::square;
double ans = square.apply(23d);
Function above is a functional interface. To fully understand ::, it is important to understand functional interfaces as well. Plainly, a functional interface is an interface with just one abstract method.
Examples of functional interfaces include Runnable, Callable, and ActionListener.
Function above is a functional interface with just one method: apply. It takes one argument and produces a result.
The reason why ::s are awesome is that:
Method references are expressions which have the same treatment as lambda expressions (...), but instead of providing a method body, they refer an existing method by name.
E.g., instead of writing the lambda body
Function<Double, Double> square = (Double x) -> x * x;
You can simply do
Function<Double, Double> square = Hey::square;
At runtime, these two square methods behave exactly the same as each other. The bytecode may or may not be the same (though, for the above case, the same bytecode is generated; compile the above and check with javap -c).
The only major criterion to satisfy is: the method you provide should have a similar signature to the method of the functional interface you use as object reference.
The below is illegal:
Supplier<Boolean> p = Hey::square; // illegal
square expects an argument and returns a double. The get method in Supplier returns a value, but it does not take an argument. Thus, this results in an error.
A method reference refers to the method of a functional interface. (As mentioned, functional interfaces can have only one method each.)
Some more examples: the accept method in Consumer takes an input, but it doesn't return anything.
Consumer<Integer> b1 = System::exit; // void exit(int status)
Consumer<String[]> b2 = Arrays::sort; // void sort(Object[] a)
Consumer<String> b3 = MyProgram::main; // void main(String... args)
class Hey {
public double getRandom() {
return Math.random();
}
}
Callable<Double> call = hey::getRandom;
Supplier<Double> call2 = hey::getRandom;
DoubleSupplier sup = hey::getRandom;
// Supplier is functional interface that takes no argument and gives a result
Above, getRandom doesn't take any argument and returns a double. So any functional interface that satisfies the criteria of: take no argument and return double can be used.
Another example:
Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate<String> pred = set::contains;
boolean exists = pred.test("leo");
In case of parameterized types:
class Param<T> {
T elem;
public T get() {
return elem;
}
public void set(T elem) {
this.elem = elem;
}
public static <E> E returnSame(E elem) {
return elem;
}
}
Supplier<Param<Integer>> obj = Param<Integer>::new;
Param<Integer> param = obj.get();
Consumer<Integer> c = param::set;
Supplier<Integer> s = param::get;
Function<String, String> func = Param::<String>returnSame;
Method references can have different styles, but fundamentally they all mean the same thing and can simply be visualized as lambdas:
A static method (ClassName::methName)
An instance method of a particular object (instanceRef::methName)
A super method of a particular object (super::methName)
An instance method of an arbitrary object of a particular type (ClassName::methName)
A class constructor reference (ClassName::new)
An array constructor reference (TypeName[]::new)
For further reference, see State of the Lambda.
Yes, that is true. The :: operator is used for method referencing. So, one can extract static methods from classes by using it or methods from objects. The same operator can be used even for constructors. All cases mentioned here are exemplified in the code sample below.
The official documentation from Oracle can be found here.
You can have a better overview of the JDK 8 changes in this article. In the Method/Constructor referencing section a code example is also provided:
interface ConstructorReference {
T constructor();
}
interface MethodReference {
void anotherMethod(String input);
}
public class ConstructorClass {
String value;
public ConstructorClass() {
value = "default";
}
public static void method(String input) {
System.out.println(input);
}
public void nextMethod(String input) {
// operations
}
public static void main(String... args) {
// constructor reference
ConstructorReference reference = ConstructorClass::new;
ConstructorClass cc = reference.constructor();
// static method reference
MethodReference mr = cc::method;
// object method reference
MethodReference mr2 = cc::nextMethod;
System.out.println(cc.value);
}
}
A lambda expression is used to create anonymous methods. It does nothing but call an existing method, but it is clearer to refer to the method directly by its name. And method reference enables us to do that using method-reference operator :: .
Consider the following simple class where each employee has a name and grade.
public class Employee {
private String name;
private String grade;
public Employee(String name, String grade) {
this.name = name;
this.grade = grade;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
}
Suppose we have a list of employees returned by some method and we want to sort the employees by their grade. We know we can make use of anonymous class as:
List<Employee> employeeList = getDummyEmployees();
// Using anonymous class
employeeList.sort(new Comparator<Employee>() {
#Override
public int compare(Employee e1, Employee e2) {
return e1.getGrade().compareTo(e2.getGrade());
}
});
where getDummyEmployee() is some method as:
private static List<Employee> getDummyEmployees() {
return Arrays.asList(new Employee("Carrie", "C"),
new Employee("Fanishwar", "F"),
new Employee("Brian", "B"),
new Employee("Donald", "D"),
new Employee("Adam", "A"),
new Employee("Evan", "E")
);
}
Now we know that Comparator is a functional interface. A functional interface is the one with exactly one abstract method (though it may contain one or more default or static methods). Lambda expression provides implementation of #FunctionalInterface so a functional interface can have only one abstract method. We can use the lambda expression as:
employeeList.sort((e1,e2) -> e1.getGrade().compareTo(e2.getGrade())); // Lambda expression
It seems all good, but what if the class Employee also provides a similar method?
public class Employee {
private String name;
private String grade;
// getter and setter
public static int compareByGrade(Employee e1, Employee e2) {
return e1.grade.compareTo(e2.grade);
}
}
In this case, using the method name itself will be more clear. Hence we can directly refer to the method by using the method reference as:
employeeList.sort(Employee::compareByGrade); // Method reference
As per the documentation, there are four kinds of method references:
+----+-------------------------------------------------------+--------------------------------------+
| | Kind | Example |
+----+-------------------------------------------------------+--------------------------------------+
| 1 | Reference to a static method | ContainingClass::staticMethodName |
+----+-------------------------------------------------------+--------------------------------------+
| 2 |Reference to an instance method of a particular object | containingObject::instanceMethodName |
+----+-------------------------------------------------------+--------------------------------------+
| 3 | Reference to an instance method of an arbitrary object| ContainingType::methodName |
| | of a particular type | |
+----+-------------------------------------------------------+--------------------------------------+
| 4 |Reference to a constructor | ClassName::new |
+------------------------------------------------------------+--------------------------------------+
:: is a new operator included in Java 8 that is used to refer to a method of an existing class. You can refer to static methods and non-static methods of a class.
For referring to static methods, the syntax is:
ClassName :: methodName
For referring to non-static methods, the syntax is
objRef :: methodName
And
ClassName :: methodName
The only prerequisite for referring to a method is that the method exists in a functional interface, which must be compatible with the method reference.
Method references, when evaluated, create an instance of the functional interface.
This was found on: http://www.speakingcs.com/2014/08/method-references-in-java-8.html
This is a method reference in Java 8. The Oracle documentation is here.
As stated in the documentation...
The method reference Person::compareByAge is a reference to a static
method.
The following is an example of a reference to an instance method of a
particular object:
class ComparisonProvider {
public int compareByName(Person a, Person b) {
return a.getName().compareTo(b.getName());
}
public int compareByAge(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
The method reference myComparisonProvider::compareByName invokes the method compareByName
that is part of the object myComparisonProvider. The JRE infers the
method type arguments, which in this case are (Person, Person).
The :: operator was introduced in Java 8 for method references. A method reference is the shorthand syntax for a lambda expression that executes just one method. Here's the general syntax of a method reference:
Object :: methodName
We know that we can use lambda expressions instead of using an anonymous class. But sometimes, the lambda expression is really just a call to some method, for example:
Consumer<String> c = s -> System.out.println(s);
To make the code clearer, you can turn that lambda expression into a method reference:
Consumer<String> c = System.out::println;
So I see here tons of answers that are frankly overcomplicated, and that's an understatement.
The answer is pretty simple: **:: is called a method references. In Method References you can find all the information if you scroll down to the table.
Now, let's take a short look at what is a method reference:
A::b somewhat substitutes the following inline lambda expression: (parameters ...) -> A.b(parameter ...)
To correlate this with your questions, it's necessary to understand a Java lambda expression. Which is not hard.
An inline lambda expression is similar to a defined functional interface (which is an interface that has no more and no less than one method).
Let's take a short look on what I mean:
InterfaceX f = (x) -> x*x;
InterfaceX must be a functional interface. Any functional interface, the only thing what's important about InterfaceX for that compiler is that you define the format:
InterfaceX can be any of this:
interface InterfaceX
{
public Integer callMe(Integer x);
}
Or this:
interface InterfaceX
{
public Double callMe(Integer x);
}
Or more generic:
interface InterfaceX<T, U>
{
public T callMe(U x);
}
Let's take the first presented case and the inline lambda expression that we defined earlier.
Before Java 8, you could've defined it similarly this way:
InterfaceX o = new InterfaceX(){
public int callMe(int x)
{
return x*x;
}
};
Functionally, it's the same thing. The difference is more in how the compiler perceives this.
Now that we took a look at inline lambda expression, let's return to the method reference (::). Let's say you have a class like this:
class Q {
public static int anyFunction(int x)
{
return x + 5;
}
}
Since method anyFunctions has the same types as InterfaceX callMe, we can equivalate those two with a method reference.
We can write it like this:
InterfaceX o = Q::anyFunction;
And that is equivalent to this:
InterfaceX o = (x) -> Q.anyFunction(x);
A cool thing and advantage of method references are that at first, until you assign them to variables, they are typeless. So you can pass them as parameters to any equivalent looking (has same defined types) functional interface. Which is exactly what happens in your case.
I found this source very interesting.
In fact, it is the lambda that turns into a double colon. The double colon is more readable.
We follow these steps:
Step 1
// We create a comparator of two persons
Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());
Step 2
// We use the interference
Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());
Step 3
// The magic using method reference
Comparator c = Comparator.comparing(Person::getAge);
The :: is known as a method reference. Let’s say we want to call a calculatePrice method of class Purchase. Then we can write it as:
Purchase::calculatePrice
It can also be seen as a short form of writing the lambda expression, because method references are converted into lambda expressions.
In older Java versions, instead of "::" or lambd, you can use:
public interface Action {
void execute();
}
public class ActionImpl implements Action {
#Override
public void execute() {
System.out.println("execute with ActionImpl");
}
}
public static void main(String[] args) {
Action action = new Action() {
#Override
public void execute() {
System.out.println("execute with anonymous class");
}
};
action.execute();
//or
Action actionImpl = new ActionImpl();
actionImpl.execute();
}
Or passing to the method:
public static void doSomething(Action action) {
action.execute();
}
Since many answers here explained well :: behaviour, additionally I would like to clarify that :: operator doesn’t need to have exactly same signature as the referring functional interface if it is used for instance variables. Let’s assume we need a BinaryOperator which has type of TestObject. In the traditional way it’s implemented like this:
BinaryOperator<TestObject> binary = new BinaryOperator<TestObject>() {
#Override
public TestObject apply(TestObject t, TestObject u) {
return t;
}
};
As you see in the anonymous implementation, it requires two TestObject arguments and returns a TestObject object as well. To satisfy this condition by using the :: operator, we can start with a static method:
public class TestObject {
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
And then call:
BinaryOperator<TestObject> binary = TestObject::testStatic;
Ok, it compiled fine. What about if we need an instance method? Let’s update TestObject with an instance method:
public class TestObject {
public final TestObject testInstance(TestObject t, TestObject t2) {
return t;
}
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
Now we can access the instance as below:
TestObject testObject = new TestObject();
BinaryOperator<TestObject> binary = testObject::testInstance;
This code compiles fine, but the below one does not:
BinaryOperator<TestObject> binary = TestObject::testInstance;
My Eclipse tells me "Cannot make a static reference to the non-static method testInstance(TestObject, TestObject) from the type TestObject ..."
Fair enough. It’s an instance method, but if we overload testInstance as below:
public class TestObject {
public final TestObject testInstance(TestObject t) {
return t;
}
public final TestObject testInstance(TestObject t, TestObject t2) {
return t;
}
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
And call:
BinaryOperator<TestObject> binary = TestObject::testInstance;
The code will just compile fine. Because it will call testInstance with a single parameter instead of a double one. OK, so what happened our two parameter? Let’s print out and see:
public class TestObject {
public TestObject() {
System.out.println(this.hashCode());
}
public final TestObject testInstance(TestObject t) {
System.out.println("Test instance called. this.hashCode:" +
this.hashCode());
System.out.println("Given parameter hashCode:" + t.hashCode());
return t;
}
public final TestObject testInstance(TestObject t, TestObject t2) {
return t;
}
public static final TestObject testStatic(TestObject t, TestObject t2) {
return t;
}
}
Which will output:
1418481495
303563356
Test instance called. this.hashCode:1418481495
Given parameter hashCode:303563356
OK, so the JVM is smart enough to call param1.testInstance(param2). Can we use testInstance from another resource but not TestObject? I.e.:
public class TestUtil {
public final TestObject testInstance(TestObject t) {
return t;
}
}
And call:
BinaryOperator<TestObject> binary = TestUtil::testInstance;
It will just not compile and the compiler will say: "The type TestUtil does not define testInstance(TestObject, TestObject)".
So the compiler will look for a static reference if it is not the same type. OK, what about polymorphism? If we remove the final modifiers and add our SubTestObject class:
public class SubTestObject extends TestObject {
public final TestObject testInstance(TestObject t) {
return t;
}
}
And call:
BinaryOperator<TestObject> binary = SubTestObject::testInstance;
It will not compile either. The compiler will still look for a static reference. But the below code will compile fine since it is passing the is-a test:
public class TestObject {
public SubTestObject testInstance(Object t) {
return (SubTestObject) t;
}
}
BinaryOperator<TestObject> binary = TestObject::testInstance;
return reduce(Math::max); is not equal to return reduce(max());.
But it means something like this:
IntBinaryOperator myLambda = (a, b)->{(a >= b) ? a : b};//56 keystrokes I had to type -_-
return reduce(myLambda);
You can just save 47 keystrokes if you write like this:
return reduce(Math::max); // Only 9 keystrokes ^_^
In Java 8, Streams Reducer works as a function which takes two values as input and returns the result after some calculation. This result is fed into the next iteration.
In case of the Math:max function, the method keeps returning the maximum of two values passed and in the end you have the largest number in hand.
The previous answers are quite complete regarding what the :: method reference does. To sum up, it provides a way to refer to a method (or constructor) without executing it, and when evaluated, it creates an instance of the functional interface that provides the target type context.
Below are two examples to find an object with the maximum value in an ArrayList with and without the use of :: method reference. Explanations are in the comments below.
Without the use of ::
import java.util.*;
class MyClass {
private int val;
MyClass (int v) { val = v; }
int getVal() { return val; }
}
class ByVal implements Comparator<MyClass> {
// no need to create this class when using method reference
public int compare(MyClass source, MyClass ref) {
return source.getVal() - ref.getVal();
}
}
public class FindMaxInCol {
public static void main(String args[]) {
ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
myClassList.add(new MyClass(1));
myClassList.add(new MyClass(0));
myClassList.add(new MyClass(3));
myClassList.add(new MyClass(6));
MyClass maxValObj = Collections.max(myClassList, new ByVal());
}
}
With the use of ::
import java.util.*;
class MyClass {
private int val;
MyClass (int v) { val = v; }
int getVal() { return val; }
}
public class FindMaxInCol {
static int compareMyClass(MyClass source, MyClass ref) {
// This static method is compatible with the compare() method defined by Comparator.
// So there's no need to explicitly implement and create an instance of Comparator like the first example.
return source.getVal() - ref.getVal();
}
public static void main(String args[]) {
ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
myClassList.add(new MyClass(1));
myClassList.add(new MyClass(0));
myClassList.add(new MyClass(3));
myClassList.add(new MyClass(6));
MyClass maxValObj = Collections.max(myClassList, FindMaxInCol::compareMyClass);
}
}
At runtime they behave exactly the same. The bytecode may not be the same (for the above in case, it generates the same bytecode (compile above and check javaap -c;)).
At runtime they behave exactly the same. The method (math::max) it generates the same math (compile the above and check javap -c;)).
The double colon, i.e., the :: operator, was introduced in Java 8 as a method reference. A method reference is a form of lambda expression which is used to reference the existing method by its name.
classname::methodName
Example:
stream.forEach(element -> System.out.println(element))
By using double colon ::
stream.forEach(System.out::println(element))

The getValue() method is overridden in two ways. Which one is correct?

My problem with the following example is that I have read that if you override a function, the return type cannot be changed unless you are overriding in a descendant object, in which case you may change the return type from ParentClass to ChildClass but that is not the case in either of these cases. We change the return type from String to Object in the first example, and from Object to String in the second one, neither of which are class A or B. Maybe it is the case that you can return a more specific type in any overridden functions?
I: This one is definitely false because you are returning a more general type when overriding.
public class Test {
public static void main(String[] args) {
A a = new A();
System.out.println(a.getValue());
}
}
class B {
public String getValue() {
return "Any object";
}
}
class A extends B {
public Object getValue() {
return "A string";
}
}
II: This must be the correct one but I don't understand because I thought you could only override from returning type B in class B to type A in class A. That that was the only exception and in previous versions of Java, even that wasn't allowed so I don't see how we can change Object to String.
public class Test {
public static void main(String[] args) {
A a = new A();
System.out.println(a.getValue());
}
}
class B {
public Object getValue() {
return "Any object";
}
}
class A extends B {
public String getValue() {
return "A string";
}
}
The second is fine, because of this rule in JLS 8.4.8.3:
If a method declaration d1 with return type R1 overrides or hides the declaration of another method d2 with return type R2, then d1 must be return-type-substitutable (§8.4.5) for d2, or a compile-time error occurs.
This rule allows for covariant return types - refining the return type of a method when overriding it.
Object is the superclass of String, so it's fine for a method previously declared to return Object is refined in a subclass override to return String.
It's fine because any code which only knows about the superclass method is going to use the return value as Object, and it's fine to use any String reference as an Object reference, e.g.
String x = ...; // Anything here
Object y = x; // No problem
It sounds like you're getting confused between the two pairs of types here::
the class declaring the method and the class overriding it
the return type in the original method and the return type in the override
Those are four completely separate classes - here B, A, Object and String respectively.
All is about Covariant return type
Take a look at this article it will help you.

Java generics (template) specialization possible (overriding template types with specific types)

I'm wondering what are the options to specialize generic types in Java, i.e. in a templated class to have specific overrides for certain types.
In my case I was a generic class (of type T) to return null usually, but return "" (the empty string), when T is the String type, or 0 (zero) when its the Integer type, etc.
Merely providing a type-specific overload of a method produces a "method is ambiguous" error:
e.g.:
public class Hacking {
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
Bar<String> barString = new Bar<String>();
// OK, returns null
System.out.println(barInt.get(new Integer(4)));
// ERROR: The method get(String) is ambiguous for the type Bar<String>
System.out.println(barString.get(new String("foo")));
}
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
}
Is the only option to subclass the generic class with a specific type (see StringBar in the following example?
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
StringBar barString2 = new StringBar();
// OK, returns null
System.out.println(barInt.get());
// OK, returns ""
System.out.println(barString2.get());
}
public static class Bar<T> {
public T get() {
return null;
}
}
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
}
Is this is the only way, it's a bit of a pain to have to create a subclass for every type I want to specialize instead of an overload of get() in the Bar class.
I'm guessing I could check the instanceof in the Bar.get() method, e.g.
T get(T t) {
if (t instanceof String) return "";
if (t instanceof Integer) return 0;
else return null;
}
However I've been taught to avoid instanceof and use polymorphism when possible.
All things considered, the concensus appears to be that the StringBar method mentioned in the question is the only way to go.
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
Generics in Java are very different from templates in C++ in this respect. It is not possible to write a specific version of a generic class to do something different for a particular case, as C++ can do. It is also not possible to determine at run time what T is - this is because that information is not passed into the byte code (object code) and so doesn't even exist at runtime. This due to something called "type erasure".
BarString and BarInt would be the obvious way of doing this, but there are improvements you can make. For example you can write a generic Bar to cover the common cases, and then write specialized BarString and BarInt to implement special cases. Ensure that the instances can only be created through a factory, which takes the class of the object to be processed:
class Bar<T> {
class BarString extends Bar<String> {
// specialist code goes here
}
static Bar<T> createBar(Class<T> clazz) {
if (clazz==String.class) {
return new BarString();
} else {
return new Bar<T>;
}
That probably won't compile, but I don't have the time to work out the exact syntax. It does illustrate the principle.
The compiler is actually correct, because the following code is compile-time checked (Bar<String> barString = new Bar<String>();) when compiled, from
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
to
public static class Bar<String> {
public String get(String x) {
return null;
}
public String get(String x) {
return "";
}
}
and is ambiguous as you can't have 2 identical methods with the same return types and the same parameter arguments.
See an explanation by Jon Skeet's:
What is the concept of erasure of generics in java?
Java Generics - Types erasures - when and what happens?
You can subclass Bar<T> and create StringBar (note I removed the static keyword) and override get() method.
public class BarString extends Bar<String> {
#Override
public String get(String x) {
return "";
}
}
Generics in Java aren't made for specialization. They're made for generalization! If you want to specialize for certain types, you should be specializing...through a subclass.
Often you don't need to do something in a specialized manner however. Your StringBar example is kind of contrived because you could have this:
public class Bar<T> {
private final T value;
public T get() {
return value;
}
}
I don't see why you need to specialize for a String here.

Categories