I've isolated the error to this line:
string.getClass() == jojo.getClass()
Shouldn't this line create two Class objects and then check if they (as in the two references) point to the same object? Rather than returning a value of false, the code won't run.
public class Tester
{
public static void main(String[] args)
{
OreoJar jojo = new OreoJar(0);
OreoJar momo = new OreoJar(1);
String string = "Hello";
if (momo.getClass() == jojo.getClass())
{
System.out.println("Momo and jojo are of the same class");
}
if (string.getClass() == jojo.getClass())
{
System.out.println("String and jojo are of the same class");
}
}
}
public class OreoJar
{
int oreos;
public OreoJar(int oreos)
{
this.oreos = oreos;
}
public void count()
{
System.out.println(oreos + " oreos in this jar!");
}
}
This comment is kind of hidden and I think its worth mentioning since it makes the most sense to a beginner (such as myself)
-According to the JLS "It is a compile-time error if it is impossible to convert the type of either operand to the type of the other by a casting conversion" so two references of types A and B can be compared if, and only if, either A can be cast to B or B can be cast to A. – Patricia Shanahan
I agree OP should quote the compilation error.
Anyway the compilation error is quite obvious when anyone actually does a compilation.
The error is:
Tester.java:15: incomparable types: java.lang.Class<capture#125 of ? extends java.lang.String> and java.lang.Class<capture#29 of ? extends OreoJar>
if (string.getClass() == jojo.getClass()){
^
Reason seems obvious.
From Javadoc of Object.getClass():
The java.lang.Class object that represents the runtime class of the
object. The result is of type Class<? extends X> where X is the
erasure of the static type of the expression on which getClass is
called.
That means, an String instance is going to return a reference to Class<? extends String>, while an OreoJar instance is going to return reference to Class<? extends OreoJar>
The two types are simply not compatible, as the compiler knows that there is no chance that any type that extends String can be a type extends OreoJar. So comparison is going to cause compilation error.
A bit off topic but I think worth mentioning, you said:
Shouldn't this line create two Class objects and then check if they point to the same object
I think it is better to have clearer understanding. It is not going to "create" two Class objects. getClass() is going to return you a reference to Class object. And, it is always a reference that can point to an object, not object that point to object (it sounds weird too)
I think the reason it won't compile is due to the fact that Class has generic component. Try using momo.getClass().equals(jojo.getClass())
And you might also try comparing the canonical names of the classes for a similar effect: momo.getClass().getCanonicalName().equals(jojo.getClass().getCanonicalName())
getClass() returns an instance of a Class. getClass().getName() returns a string. The String.equals(otherString) method is the correct way to compare Strings for equality.
Related
I have this:
class Male extends Person{..}
class Women extends Person{..}
// returns true if the return type of the given function is of type Women
boolean isReturnTypeWomen(Function<Integer, Person> function){
Class<?> type = function.getClass().getMethods()[0].getReturnType();
return type.isInstance(Women.class) ? true : false;
}
However, it always return true because type is always Object.
How can I check the instance type of the second parameter (the return type) of the given Function)?
Some Function subclasses will provide an apply method with covariant return type and a synthetic bridge to forward invocation across. However, Function instances will typically be lambdas or function references, so will not.
Consider, as a counterexample, the following method.
public static <T,R> Function<T,R> nothing(Function<T,R> fun) {
return t -> fun.apply(t);
}
The Function returned by this method will always be of the same class, but T may change. Therefore the class cannot reference the type.
A benefit of this is that the function objects do not need to carry around the overhead of extra data to represent type arguments, which are of no use in type-safe code anyway.
It can't not be - the parameter is a Function<Integer, String>.
getMethods[0] doesn't work. For example:
public class Example implements Function<Integer, String> {
public void completelyUnrelatedMethod() {}
public String apply(Integer i) { return null; }
}
This will return void here.
More generally there is no need for that runtime check - the generics take care of it:
Function<Integer, Integer> ohDearThisDoesNotReturnAString = a -> a;
isReturnTypeString(ohDearThisDoesNotReturnAString);
The above doesn't even compile.
To do a deep dive on your actual code, which utterly does not work:
Generics are erased, which mostly means that generics are usually a figment of the compiler's imagination. At runtime the information is simply gone. This isn't always true, but it usually is or can always be, so what you want (a runtime check) is impossible to guarantee.
In addition, you've messed up the check. isInstance does what instanceof does: It requires a type and some object. What you have is 2 instances of Class<?>; the right method for this is isAssignableFrom, not isInstance. As you have discovered, it doesn't work and cannot work - that's that erasure thing.
I have added three methods with parameters:
public static void doSomething(Object obj) {
System.out.println("Object called");
}
public static void doSomething(char[] obj) {
System.out.println("Array called");
}
public static void doSomething(Integer obj) {
System.out.println("Integer called");
}
When I am calling doSomething(null) , then compiler throws error as ambiguous methods. So is the issue because Integer and char[] methods or Integer and Object methods?
Java will always try to use the most specific applicable version of a method that's available (see JLS §15.12.2).
Object, char[] and Integer can all take null as a valid value. Therefore all 3 version are applicable, so Java will have to find the most specific one.
Since Object is the super-type of char[], the array version is more specific than the Object-version. So if only those two methods exist, the char[] version will be chosen.
When both the char[] and Integer versions are available, then both of them are more specific than Object but none is more specific than the other, so Java can't decide which one to call. In this case you'll have to explicitly mention which one you want to call by casting the argument to the appropriate type.
Note that in practice this problem occurs far more seldom than one might think. The reason for this is that it only happens when you're explicitly calling a method with null or with a variable of a rather un-specific type (such as Object).
On the contrary, the following invocation would be perfectly unambiguous:
char[] x = null;
doSomething(x);
Although you're still passing the value null, Java knows exactly which method to call, since it will take the type of the variable into account.
Each pair of these three methods is ambiguous by itself when called with a null argument. Because each parameter type is a reference type.
The following are the three ways to call one specific method of yours with null.
doSomething( (Object) null);
doSomething( (Integer) null);
doSomething( (char[]) null);
May I suggest to remove this ambiguity if you actually plan to call these methods with null arguments. Such a design invites errors in the future.
null is a valid value for any of the three types; so the compiler cannot decide which function to use. Use something like doSomething((Object)null) or doSomething((Integer)null) instead.
Every class in Java extends Object class.Even Integer class also extends Object. Hence both Object and Integer are considered as Object instance. So when you pass null as a parameter than compiler gets confused that which object method to call i.e. With parameter Object or parameter Integer since they both are object and their reference can be null. But the primitives in java does not extends Object.
I Have tried this and when there is exactly one pair of overloaded method and one of them has a parameter type Object then the compiler will always select the method with more specific type. But when there is more than one specific type, then the compiler throws an ambiguous method error.
Since this is a compile time event, this can only happen when one intentionally passes null to this method. If this is done intentionally then it is better to overload this method again with no parameter or create another method altogether.
class Sample{
public static void main (String[] args) {
Sample s = new Sample();
s.printVal(null);
}
public static void printVal(Object i){
System.out.println("obj called "+i);
}
public static void printVal(Integer i){
System.out.println("Int called "+i);
}
}
The output is Int called null and so ambiguity is with char[] and Integer
there is an ambiguity because of doSomething(char[] obj) and doSomething(Integer obj).
char[] and Integer both are the same superior for null that's why they are ambiguous.
Here's the code:
public class Main {
public static void main(String[] args) {
Gen<Integer> g = new Gen<Integer>(5);
System.out.println(g.getClass());
System.out.println(g.ob.getClass());
}
}
class Gen<T> {
T ob;
public Gen(T x) {
ob = x;
}
}
And here's the output
class Gen // This I understand
class java.lang.Integer // But if type erasure is happening, shouldn't this be java.lang.Object?
I get it that Type parameter T is erased at runtime, but then why is the type parameter of ob surviving at runtime?
Nope!
Consider this:
Object x = new Integer(1);
System.out.println(x.toString());
You'll get 1.
But shouldn't I get Object.toString()?
No. While x is a reference of type Object, the actual referent is an Integer, so at run-time, the Integer implementation of toString is called.
It is the same for getClass.
Type erasure is happening. Generics are a compile time type checking system. At run-time you still get the class (it is run-time type information). The linked Type erasure documentation says (in part)
Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Your instance has a type, it's Object. But an Object reference can refer to any sub-class (which is every class) in Java. You get the type it refers to.
No matter what type the variable has, the return value of getClass() depends on the variable's contents. So, since you basically have a variable Object ob which contains an Integer (your int was converted to it at the time you provided it as that constructor's parameter), the output of ob.getClass() is class java.lang.Integer.
Also, about your question as to why getClass() remembers the type argument: it doesn't. All it does is determine the content's class. For example:
class Foo {
public Foo() {}
}
class Bar extends Foo {
public Bar() {}
}
class Baz<T> {
public T object;
public Baz(T object) { this.object = object; }
}
If you now run the following snippet...
public static void main(String... args) {
Baz<Foo> obj = new Baz(new Bar());
System.out.println(obj.object.getClass());
}
You will notice that the output is not class Foo, it's class Bar.
Because when compiled, class Gen has an Object ob; The generics disappear from the final product. The angle brackets only play a role at compile-time, during static type checking. It's something the compiler can do for you to give you better peace of mind, to assure you that you're using collections and other paramterized types correctly.
the actual object assigned to ob at runtime is an instance of the Integer class, and ob.getClass() serves the purpose to find out the actual class of the object referenced by the pointer -> hence you will see java.lang.Integer printed.
Remember, what comes out is effectively class Gen { Object ob; ... }
Since my first exposure to generics was with C# , it took time get a hold of what type erasure is in java.
But after better understanding java generics , i realized that in my question i'm mixing 2 separate topics : Generics and Reflection.
The main question was , why did the second call here
System.out.println(g.getClass());
System.out.println(g.ob.getClass());
returned java.lang.Integer instead of java.lang.Object.
Looking at the docs for getClass() , the answer becomes obvious
Returns the runtime class of this Object.
so, getClass() doesn't return the type of reference but the actual object being referred to by the reference.
For ex :
Object o = "abc";
System.out.println(o.getClass());
The output wouldn't be the type of the reference java.lang.Object but rather the actual type of the object java.lang.String.
Simply the just as the question have stated. The answer should ideally be false, since it would be using the Object#equal which is simply reference comparison.
String cat = new String("cat");
String cat2 = new String("cat");
System.out.println(((Object) cat).equals((Object) cat2)); // returns true, but should be false
This has to do with polymorphism; I know how equals() and interning work.
RELATED TOPICS: CASTING GRAPHICS -> GRAPHICS2D
The aforementioned scenario is a case of upcasting where String is being downcasted to Object.
However, a common use of this is actually downcasting Graphics to Graphics2D to use upgraded or new method that don't exist in Graphics itself. How come we can upcast and NOT downcast.
import java.awt.*;
import javax.swing.*;
public class Example extends JPanel {
public static void main (String []args){
JFrame frame = new JFrame();
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g; // How can we be sure the informal
g2.drawLine(0,0, getWidth(), getHeight()); // parameter contains those methods?
}
}
You cast cat to Object, but that doesn't change the fact that cat is a String instance. Java performs dynamic binding (aka late binding), meaning that method invocations are resolved at runtime based on the class of which the object is an instance. In this case, that's still String, which means String#equals() will be used, not Object#equals(). The fact that you cast cat2 to Object makes little difference -- equals() takes an Object argument anyway.
Here's an easy way to prove this to yourself:
String s = "abc";
Object o = (Object) s; // we don't really need an explicit cast
System.out.println(s.getClass());
System.out.println(o.getClass());
class java.lang.String
class java.lang.String
The same principle is at work here. o is of type Object but it is a String instance, and so String's getClass() is called.
Consider the following class structure:
class A {
public void foo() {
System.out.println("A foo");
}
}
class B extends A {
public void foo() {
System.out.println("B foo");
}
public void bar() {
System.out.println("B bar");
}
}
Now, when we have something like
A b = new B();
b.foo();
then B's foo method will be invoked. This is the phenomena described above. You're asking about something like:
A b = new B();
b.bar(); // error
The reason we have an error here is because there is no guarantee that b will have a bar() method. For all we know (or, rather, the compiler knows), b could be a C instance with who-knows-what. For this reason, we have to perform an explicit cast:
A b = new B();
((B) b).bar();
This follows from the fact that Java is statically typed (even though it performs dynamic binding).
Instance methods are resolved at run time based on their dynamic type. This is polymorphism/late binding. Regardless of you casting the object to a static type of Object, it's runtime type is still String, so String#equals(Object) will be used.
How come we can upcast and NOT downcast.
Given two classes
public class A {
}
public class B extends A {
public void doSomething() {}
}
and
A a = new B();
a.doSomething(); // this won't compile because A doesn't declare a doSomething() method
((B) a).doSomething(); // this is fine because you are now calling the method on the static type B
// you might get a ClassCastException here if the runtime type of a wasn't B
As all the methods are default virtual in java so even though you type casted it to Object it will call String equals method because the object is of type String not of Object.
This is also called late binding or runtime polymorphism.
That's not how Java resolves which method should be used. Even though you are casting it to Object at runtime String's equals will be used. And String's equals doesn't check reference equality but if the strings are "logically" equal.
This is due to so called "dynamic linking" you can google it or try reading about it here.
String Object is one of the immutable objects, others like Integer.
So here is a simple comparison, you created 2 Integer objects(the same value) and do equal operation. It makes more sense returning the 2 Integers are equal.
And from the example, I guess you might now know the objective of immutable classes: they are designed to retain their state and simplify operations relate to them, just like an int.
I was wondering what is the difference between the following two method declarations:
public Object doSomething(Object obj) {....}
public <T> T doSomething(T t) {....}
Is there something you can/would do with one but not the other? I could not find this question elsewhere on this site.
Isolated from context - no difference. On both t and obj you can invoke only the methods of Object.
But with context - if you have a generic class:
MyClass<Foo> my = new MyClass<Foo>();
Foo foo = new Foo();
Then:
Foo newFoo = my.doSomething(foo);
Same code with object
Foo newFoo = (Foo) my.doSomething(foo);
Two advantages:
no need of casting (the compiler hides this from you)
compile time safety that works. If the Object version is used, you won't be sure that the method always returns Foo. If it returns Bar, you'll have a ClassCastException, at runtime.
The difference here is that in the first, we specify that the caller must pass an Object instance (any class), and it will get back another Object (any class, not necessarily of the same type).
In the second, the type returned will be the same type as that given when the class was defined.
Example ex = new Example<Integer>();
Here we specify what type T will be which allows us to enforce more constraints on a class or method. For example we can instantiate a LinkedList<Integer> or LinkedList<Example> and we know that when we call one of these methods, we'll get back an Integer or Example instance.
The main goal here is that the calling code can specify what type of objects a class will operate upon, instead of relying on type-casting to enforce this.
See Java Generics* from Oracle.
*Updated Link.
The difference is that with generic methods I don't need to cast and I get a compilation error when I do wrong:
public class App {
public static void main(String[] args) {
String s = process("vv");
String b = process(new Object()); // Compilation error
}
public static <T> T process(T val) {
return val;
}
}
Using object I always need to cast and I don't get any errors when I do wrong:
public class App {
public static void main(String[] args) {
String s = (String)process("vv");
String b = (String)process(new Object());
}
public static Object process(Object val) {
return val;
}
}
You don't need to do additional class casting. In first case you will always get an object of class java.lang.Object which you will need to cast to your class. In second case T will be replaced with the class defined in generic signature and no class casting will be needed.
At runtime, nothing. But at compile time the second will do type checking to make sure the type of the parameter and the type of the return value match (or are subtypes of) whatever type T resolves to (the first example also does type checking but every object is a subtype of Object so every type will be accepted).
T is a generic type. Meaning it can be substituted by any qualifying object at runtime. You may invoke such a method as follows:
String response = doSomething("hello world");
OR
MyObject response = doSomething(new MyObject());
OR
Integer response = doSomething(31);
As you can see, there is polymorphism here.
But if it is declared to return Object, you can't do this unless you type cast things.
in the first case it takes a parameter of any type e.g.string and return a type foo. In the second case it takes a parameter of type foo and returns an object of type foo.
There are few reasons that you can consider Generics over Object type in Java:
Generics is flexible and safe. At the same time, working with Object that requires type-casting is error-prone
Type Casting in Java is slow
ref : [1]: https://www.infoworld.com/article/2076555/java-performance-programming--part-2--the-cost-of-casting.html