Exact type match in Java generics - java

The following Java code is valid - no compile- or run-time errors.
public class Test {
public static void main(String[] args) {
System.out.println(
new Comparator<>("hello", 5).areEqual()
);
}
private static class Comparator<T> {
private final T left;
private final T right;
public Comparator(T left, T right) {
this.left = left;
this.right = right;
}
public boolean areEqual() {
return left.equals(right);
}
}
}
I expect the values "hello" and 5 are casted to Object, which is a common parent.
Is there a way to write the class in such a way that passing other than objects of the same type causes a compile-time error? I.e. passing objects casted to a common ancestor not allowed.

Not really, no.
Because your generic type is unbound, there's only one common ancestor: Object - as you correctly surmise.
The only thing you can realistically do in this scenario is ensure that equals is correctly defined for objects you want to pass through this method. You would also want to handle cases in which you pass null as an argument, too.

Just specify the type in the generic parameters...
public class Test {
public static void main(String[] args) {
System.out.println(
new Comparator<String>("hello", 1).areEqual()
);
}
...
}
If you do this with your current code, you will get a compiler error.
When you leave it unspecified, Java does not bother doing any type checking and simply uses the super type (Object) as the type for both.
You will also find the same issue when you do something like:
System.out.println(Arrays.asList("hello", 1)); // Will compile
vs
System.out.println(Arrays.<String>asList("hello", 1)); // Will not compile

So, I think it is not possible by using generic. But you can restrict it by checking the Object type in the constructor like below code.
public Comparator(T left, T right) {
if(left.getClass()!=right.getClass()) {
throw new IllegalArgumentException("All values passed needs to be object of same class");
}
this.left = left;
this.right = right;
}
However, this will not give you compile time error but it will through Exception when a different type of object passed to the constructor.

As #Makoto already answered, the short answer is no.
If you look at code snippet for equals method in your favorite IDE then you'll see that it has line like if (getClass() != another.getClass()) return false; It means that you check class of object inside equals. You could do the same at areEqual method but probably it's not needed - it should be implemented on another level

Related

Can Java generics be parameterized with values instead of types?

Assume I want to define types that are similar in structure, but differ in a parameter that could be an integer or could be something else.
Is it possible in Java to define a family of classes parameterized by an integer or even an arbitrary object?
Consider the following pseudocode (which does not compile):
/**
* String of a certain length n and a method to reduce to length n-1
*/
public class StringN<int n> {
private String str;
public StringN( String str) {
if(str.length() != n) {
throw new IllegalArgumentException("string is not of required length!");
}
this.str = str;
}
public StringN<n-1> reduce() {
return new StringN<n-1>(s.substring(0, s.length() - 1));
}
#Override
public String toString() {
return str;
}
}
Other even more natural examples that come to my mind are tensor-products in math, so where to put the parameter 'n', if one wants to define e.g. the space R^n as a Java class or in functional programming the 'arity' of a Function<>-space. So how to define a family of classes with different arity, parameterized by n?
If this is not possible in Java, does this concept exist in other more functional languages and what is the proper name for it? (like maybe 'parameterized class'?)
Edit: as a reaction to comments, the last part was just to know the general name of such a concept, not to make a detour to other languages.
Alas, Java requires type parameters to be types (actually, it even requires them to be reference types), and since all integers are of the same type, you not get the compiler to distinguish generics depending on the value of an integer.
The usual workaround is to declare a separate type for each possible (or needed) value. To share structure, you can use an abstract base class. And if the base class needs any concrete types, the subclasses can pass them as type parameters:
abstract class StringN<S extends StringN<S,P>, P extends StringN<P,?>>
implements Comparable<S> {
final String value;
protected StringN(String value, int n) {
if (value.length() != n) {
throw new IllegalArgumentException(value);
}
this.value = value;
}
#Override
public int compareTo(S o) {
return value.compareTo(o.value);
}
abstract P newP(String value);
public P removeLast() {
return newP(value.substring(0, value.length() - 1));
}
}
class String0 extends StringN<String0, String0> {
protected String0(String value) {
super(value, 0);
}
#Override
String0 newP(String value) {
throw new UnsupportedOperationException();
}
}
class String1 extends StringN<String1, String0> {
protected String1(String value) {
super(value, 1);
}
#Override
String0 newP(String value) {
return new String0(value);
}
}
class String2 extends StringN<String2, String1> {
protected String2(String value) {
super(value, 2);
}
#Override
String1 newP(String value) {
return new String1(value);
}
}
public class Test {
public static void main(String[] args) {
String2 s2 = new String2("hi");
String1 s1 = s2.removeLast();
s1.compareTo(s2); // compilation error: The method compareTo(String1) is not applicable for the arguments (String2)
}
}
As you can see, as long as the set of values is finite and known up front, you can even teach the compiler to count :-)
However, it gets rather unwieldy and hard to understand, which is why such workarounds are rarely used.
Yours is an interesting question, but I think you went too far in assuming that the solution to your need is necessarily a parametrized class.
Parametrized classes are composition of data types, not values.
Since you do not require the compile to enforce any additional static type checkings on your code, I think a programmatic solution would be enough:
First step: Move your pseudo-parameter "int n" to a final variable:
public class StringN {
private final int n;
private String str;
public StringN( String str) {
if(str.length() != n) {
throw new IllegalArgumentException("string is not of required length!");
}
this.str = str;
}
public StringN reduce() {
return new StringN(s.substring(0, s.length() - 1));
}
#Override
public String toString() {
return str;
}
}
Of course, this do not compile yet. You must initialize the n variable on every constructor (declarations and callings).
If you feel uncomfortable with the fact of exposing the parameter n as part of the public constructors calling, that can be solved restricting the constructors to package access, and bringing the construction responsibility to a new Factory class, which must be the only public way to create StringN objects.
public StringNFactory
{
private final int n;
public StringNFactory(int n)
{
this.n=n;
}
public StringN create(String s)
{
return new StringN(this.n, s);
}
}
As the name suggests, a "type parameter" is a type. Not 'a length of a string'.
To be specific: One can imagine the concept of the type fixed length string, and one can imagine this concept has a parameter, whose type is int; one could have FixedString<5> myID = "HELLO"; and that would compile, but FixedString<5> myID = "GOODBYE"; would be an error, hopefully a compile-time one.
Java does not support this concept whatsoever. If that's what you're looking for, hack it together; you can of course make this work with code, but it means all the errors and checking occurs at runtime, nothing special would occur at compile time.
Instead, generics are to give types the ability to parameterize themselves, but only with a type. If you want to convey the notion of 'A List... but not just any list, nono, a list that stores Strings' - you can do that, that's what generics are for. That concept applies only to types and not to anything else though (such as lengths).
Furthermore, javac will be taking care of applying the parameter. So you can't hack it together by making some faux hierarchy such as:
public interface ListSize {}
public interface ListIsSizeOne implements ListSize {}
public interface ListIsSizeTwo implements ListSize {}
public interface ListIsSizeThree implements ListSize {}
and then having a FixedSizeList<T extends ListSize> so that someone can declare: FixedSizeList<ListIsSizeTwo> list = List.of(a, b);.
The reason that can't work is: You can't tell javac what to do, it's not a pluggable system. Java 'knows' how to apply type bounds. It wouldn't know how to enforce size limits, so you can't do this.
I'm answering the question myself, because the useful information is distributed over several comments/answers. I made this a community-wiki answer, so that I don't earn reputation for suggestions of others.
The feature I'm looking for is apparently a particular case of so-called dependent-typing (thanks #DylanSp). Also template parameters of C++ (with the parameter not being a type) are an example of such a feature (thanks #Turing85). All answers agree that this feature unfortunately does not exist in Java, neither within the syntax of Java Generics (#rzwitserloot and others pointed out that Java specification allows only reference types in the diamond <>), nor any other syntax.
One certainly can manually define types in Java for each particular n. So for my example in my question, one can define classes String1, String2, String3, ..., but only finitely many ones. In order to make the definition of each particular type as simple as possible, one can use an approach with an abstract base class that is shared by all of these classes, see #meriton's nice suggestion.
Not what I was thinking of, but with finitely many cases also a code generator (mentioned by #Hulk) should be an option. If I understand correctly that's also what #MC Emperor had in mind when mentioning annotations.
However, if one really wants to stick to infinitely many classes (that's what I want), the only way out seems to be, to make the counter n a member of a single class and just think of them being different types. At compiler-level, there won't be any type-checking, so one has to implement type-safety oneself. The suggestion with the factory made by #Little Santi would be a way to bring more structure into this approach.

Why can't I change the return type of overriden methods (other than covariant return type)? [duplicate]

This question already has answers here:
why we can't override a method and define it to return a superclass of the original method?
(3 answers)
Closed 3 years ago.
PLEASE NOTE - I am asking WHY? It would be very useful if you could give an example where changing the return type actually breaks the code
why can't I change the return type of an overridden method (other than covariant return types).
class Parent{
public void sayhello(){ ... };
}
class Child extends Parent{
public String sayhello() { . . .}
}
Now if I run the following code:
class test{
public static void main(String[] args){
Parent p = new Child();
p.sayHello();
}
}
Cam someone please confirm if the following steps are happening:
Compiler finds out the type of object 'p' which is Parent.
Compiler checks if method 'sayHello()' is present in Parent class.
During Runtime, JVM finds out that it is a Child object and calls child version of the method.
Child method is called.
Thanks.
Let's use a simple example to explain why it doesn't make any sense to change the return type of an overridden method.
Suppose I have a Car object:
class Car {
public String getModel() {
return "Awesome Car";
}
}
This Car class has a method getModel() that returns a String.
Now, you have another class that extends Car and overrides the getModel() method:
class DumbCar extends Car {
#Override
public Hamburger getModel() {
return new Hamburger();
}
}
Suddenly, you have a major problem. You create a DumbCar object and since you know that all Car objects can tell you their model, you try to get that model:
DumbCar myCar = new DumbCar();
System.out.println(myCar.getModel());
The output is A juicy Big Mac!
Does that make any sense to you? You cannot drive a Big Mac.
Java is a heavily type-safe language. When you write a statement asking to getModel(), you need to be absolutely, 100% positive, that the data you get back is what you were expecting. Java enforces that expectation.
Java is a statically typed language.
This means that the compiler checks that all types make sense before the program is even run. So you will not get errors at runtime because some method or field is not "there".
In order for this to work if you have code that says
MyBean x = something.getMyBean();
it cannot be allowed for a subclass of what the compiler has determined the type of something to be to change the return type of getMyBean() to something other than MyBean (subclasses of MyBean are also allowed, this is called narrowing the return type -- but even that was not possible before Java 5).
The problem is basically that such a thing would make the Java type system unsound, and since Java has a statically-typed system this cannot be allowed.
Suppose you had an Expression interface:
interface Expression {
Integer evaluate();
}
And now you have an Addition implementation:
class Addition implements Expression {
private Expression left;
private Expression right;
Addition(Expression left, Expression right) {
this.left = left;
this.right = right;
}
#Override
public Integer evaluate() {
return left.evaluate() + right.evaluate();
}
}
This works as long as expressions evaluate to Integers, e.g.
class Constant implements Expression {
private Integer value;
Constant(Integer value) {
this.value = value;
}
#Override
public Integer evaluate() {
return this.value;
}
}
Which allows us to do things like:
Expression left = new Constant(1);
Expression right = new Constant(2);
Expression addition = new Addition(left, right);
Integer result = addition.evaluate();
What would happen now if you had an expression that instead of evaluating to an Integer evaluated to something else that is not an expression, like a Cat or a Dog?
It would immediately break the soundness of every other expression you had written in the past like that of the last example or the obvious assumptions we made in the Addition.evaluate method where we assumed that left and right expressions returned Integer not Cats or Dogs.

Parameterize a class with one of a fixed set of types

Say I have a generic class Foo which can hold an object of type T. Furthermore, let's say I only want to be able to instantiate the class with objects that are one of two types. Finally, let's say that the lowest common upper bound of these two types is a type that has many more subclasses than those two types that I want to allow, so I can't simply specify an upper bound for the type parameter (as in class Foo<T extends Something>), because then I would allow to instantiate the class with other types than the two I expect.
For illustration, let's say I want Foo to hold only either a String or an Integer. The lowest common upper bound is Object, so specifying an upper bound won't do the trick.
Certainly, I could do something along the lines of
class Foo<T> {
private T obj;
public Foo(T obj) throws IllegalArgumentException {
if (!(obj instanceof String || obj instanceof Integer)) {
throw new IllegalArgumentException("...");
}
this.obj = obj;
}
}
However, in this case, I can still call the constructor with any object; if I try to instantiate it with something that is neither a String nor an Integer, I will get an exception at runtime.
I would like to do better. I would like the compiler to infer statically (i.e., at compile time) that I can only instantiate this class with objects that are either String or Integer.
I was thinking something along those lines might do the trick:
class Foo<T> {
private T obj;
public Foo(String s) {
this((T) s);
}
public Foo(Integer i) {
this((T) i);
}
private Foo(T obj) {
this.obj = obj;
}
}
This works, but it looks really, really odd. The compiler warns (understandably) about unchecked casts. Of course I could suppress those warnings, but I feel this is not the way to go. In addition, it looks like the compiler can't actually infer the type T. I was surprised to find that, with the latter definition of class Foo, I could do this, for instance:
Foo<Character> foo = new Foo<>("hello");
Of course, the type parameter should be String here, not Character. But the compiler lets me get away with the above assignment.
Is there a way to achieve what I want, and if yes, how?
Side question: why does the compiler let me get away with the assignment to an object of type Foo<Character> above without even so much as a warning (when using the latter definition of class Foo)? :)
Try using static factory method to prevent compiler warning.
class Foo<T> {
private T obj;
public static Foo<String> of(String s) {
return new Foo<>(s);
}
public static Foo<Integer> of(Integer i) {
return new Foo<>(i);
}
private Foo(T obj) {
this.obj = obj;
}
}
Now you create instance using:
Foo<String> foos = Foo.of("hello");
Foo<Integer> fooi = Foo.of(42);
Foo<Character> fooc = Foo.of('a'); // Compile error
However the following are still valid since you can declare a Foo of any type T, but not instantiate it:
Foo<Character> fooc2;
Foo<Character> fooc3 = null;
Foo<Object> fooob1;
Foo<Object> fooob2 = null;
one word: interface. You want your Z to wrap either A or B. Create an interface implementing the smallest common denominator of A and B. Make your A and B implement that interface. There's no other sound way to do that, AFAIK. What you already did with your constructors etc. is the only other possibility, but it comes with the caveats you already noticed (having to use either unchecked casts, or static factory wrappers or other code smells).
note: If you can't directly modify A and/or B, create wrapper classes WA and WBfor them beforehand.
example:
interface Wrapper {
/* either a marker interface, or a real one - define common methods here */
}
class WInt implements Wrapper {
private int value;
public WInt( int value ) { this.value = value; }
}
class WString implements Wrapper {
private String value;
public WString( String value ) { this.value = value; }
}
class Foo<T> {
private Wrapper w;
public Foo(Wrapper w) { this.w = w; }
}
because you call your private Foo(T obj) due to diamond type inference. As such, it's equal to calling Foo<Character> foo = new Foo<Character>("hello");
Long story short: You are trying to create a union of two classes in java generics which is not possible but there are some workarounds.
See this post
Well the compiler uses the Character class in T parameter. Then the String constructor is used where String is casted to T (Character in this case).
Trying to use the private field obj as a Character will most likely result in an error as the saved value is an instance of the final class String.
Generics is not suitable here.
Generics are used when any class can be used as the type. If you only allow Integer and String, you should not use generics. Create two classes FooInteger and FooString instead.
The implementations should be pretty different anyway. Since Integers and Strings are very different things and you would probably handle them differently. "But I am handling them the same way!" you said. Well then what's wrong with Foo<Double> or Foo<Bar>. If you can handle Integer and String with the same implementation, you probably can handle Bar and Double and anything else the same way as well.
Regarding your second question, the compiler will see that you want to create a Foo<Character>, so it tries to find a suitable overload. And it finds the Foo(T) overload to call, so the statement is perfectly fine as far as the compiler is concerned.

Calling a function on an enum constant obtained by values()

I'm trying to generalise some code by iterating over all constants of an enum to receive the same specific argument from each one.
Specifically I have an enum P with some constants A,B,C.
Each of these constants is itself an enum and implements an interface I that defines a function f.
P.values() gives me an array P[] A = {A,B,C}, however I can't call A[i].f() since A[i] is of course of type P which doesn't implement I.
Now in my understanding a function can return an interface, but I can not instantiate it and therefore can't cast to it.
Should I overwrite values() for P to return I[]? If so, how would I do that since I can't cast to I? Or is there another solution?
I am working in eclipse but assuming that it's complaints are indicative of a true mistake, not just eclipse not recognising types.
Since I'm somewhat new to Java I would also appreciate any links to resources that explain the underlying rules of type matching/checking.
This seems to do what you describe - perhaps I have misunderstood your question though. If so please explain further.
interface I {
void f ();
}
enum P implements I{
A,
B,
C {
// Demonstrate enum-specific implementation.
#Override
public void f () {
System.out.println("SEEEEEE!");
}
};
// By default `f` prints the name of the enum.
#Override
public void f () {
System.out.println(name());
}
}
public void test() throws Exception {
for ( I i : P.values()) {
i.f();
}
}

Why does the JVM allow be to pass a B[] to a method that expects an A[]?

I have the following generic test class:
public class BrokenGenerics<T> {
private T[] genericTypeArray;
public BrokenGenerics(T... initArray) {
genericTypeArray = initArray;
}
public void setArray(T[] newArray) {
genericTypeArray = newArray;
}
public T get(int idx) {
return genericTypeArray[idx];
}
public Class getType() {
return genericTypeArray.getClass().getComponentType();
}
public static boolean breakThis(BrokenGenerics any) {
any.setArray(new B[]{new B(2)});
return false;
}
public static void main(String[] args) {
BrokenGenerics<A> aBreaker = new BrokenGenerics<A>(new A("1"));
System.out.println(aBreaker.get(0));
System.out.println(aBreaker.getType());
breakThis(aBreaker);
System.out.println(aBreaker.get(0));
System.out.println(aBreaker.getType());
}
private static class A {
public String val;
public A(String init) {
val = init;
}
#Override
public String toString() {
return "A value: " + val;
}
}
private static class B {
public int val;
public B(int init) {
val = init;
}
#Override
public String toString() {
return "B value: " + val;
}
}
}
When I run it, I get this output, and no errors:
A value: 1
class BrokenGenerics$A
B value: 2
class BrokenGenerics$B
Now, I understand why this compiles; it can't know at compile-time that breakThis is being passed a generic of a bad type. However, once it runs the line any.setArray(new B[]{new B(2)});, shouldn't it throw a ClassCastException (NOTE THAT IT DOES NOT! Try it yourself!) because I'm trying to pass a B[] to a method that expects an A[]? And after that, why does it allow me to get() back the B?
After Type Erasure, T will be turned into Object since you didn't specify a bound on T. So, there is no problem at runtime assigning any type of array to genericTypeArray, which is now of type Object[] or calling the function setArray(...), which now also accepts an argument of type Object[]. Also, your get(...) method will simply return an Object.
Trouble starts when you access elements in the array with a wrong type expectation, since this might lead to (implicit or explicit) illegal type casts, for example by assigning the value returned by get(...) to a variable of type A.
You can also get a run-time ClassCastException if you try to type-cast the array itself, but, in my experience, that is a case that tends to come up less often, although it can be very obscure to find or even understand if it does happen. You can find some examples below.
All generics-checking happens only at compile-time. And if you use raw types, these checks can not be performed rigorously, and thus the best the compiler can do is to issue a warning to let you know that you are giving up an opportunity for more meaningful checks by omitting the type argument.
Eclipse with its standard settings (and probably the java compiler with the correct flags) shows these warnings for your code:
"Class is a raw type" where you define getType() (somewhat unrelated to your question)
"BrokenGenerics is a raw type" where you define breakThis(...)
"Type safety: The method setArray(Object[]) belongs to the raw type
BrokenGenerics" where you call setArray(...) inside breakThis(...).
Examples for causing ClassCastException due to illegal type-cast of the array:
You can get ClassCastExceptions at runtime if you expose the array to the outside world (which can often be a dangerous thing to do, so I try to avoid it) by adding the following to BrokenGenerics<T>:
public T[] getArray() {
return genericTypeArray;
}
If you then change your main method to:
BrokenGenerics<A> aBreaker = new BrokenGenerics<A>(new A("1"));
A[] array = aBreaker.getArray();
System.out.println(array[0]);
System.out.println(aBreaker.getType());
breakThis(aBreaker);
array = aBreaker.getArray(); // ClassCastException here!
System.out.println(array[0]);
System.out.println(aBreaker.getType());
You get the ClassCastException at runtime at the indicated position due to a cast of the array itself rather than one of its elements.
The same thing can also happen if you set the variable genericTypeArray to protected and use it from code that subclasses your generic class with a fixed type argument:
private static class C extends BrokenGenerics<A> {
public C(A... initArray) {
super(initArray);
}
public void printFirst() {
A[] result = genericTypeArray; // ClassCastException here!
System.out.println(result[0]);
}
}
To trigger the exception, add the following to you main method:
C cBreaker = new C(new A("1"));
cBreaker.printFirst();
breakThis(cBreaker);
cBreaker.printFirst();
Imagine this case coming up in a bigger project... How on earth would you even begin to understand how that line of code could possible fail?!? :) Especially since the stack trace might be of very little help trying to find the breakThis(...) call that is actually responsible for the error.
For more in-depth example cases, you can take a look at some tests I did a little while back.
shouldn't it throw a ClassCastException because I'm trying to pass a B[] to a method that expects an A[]?
No. As this post explains, your invocation of setArray in
public static boolean breakThis(BrokenGenerics any) {
any.setArray(new B[]{new B(2)});
return false;
}
is done on a reference expression of the raw type BrokenGenerics. When interacting with raw types, all corresponding generic parameters are erased. So setArray is actually expecting a Object[]. A B[] is a Object[].
why does it allow me to get() back the B?
Assuming you're asking about this
System.out.println(aBreaker.get(0));
PrintStream#println(Object) expects an Object, not an A. As such, there is no reason for the compiler to insert a cast here. Since there is no cast, there is no ClassCastException.
If you had instead done
A a = aBreaker.get(0);
or had a method like
void println(A a) {}
...
println(aBreaker.get(0));
then these would cause ClassCastException. In other words, the compiler will insert a cast (checkcast) anywhere a type needs to be converted from a generic type parameter. That was not the case with PrintStream#println.
Similarly,
System.out.println(aBreaker.getType());
doesn't even involve the generic parameter declared in BrokenGenerics
public Class getType() {...}
and also returns a value of the raw type Class. The compiler has no reason to add a checkcast to A.

Categories