I'm reading an article by Brain Goetz. I had a doubt regarding factory method for the below code.
public class BoxImpl<T> implements Box<T> {
public static<V> Box<V> make() {
return new BoxImpl<V>();
}
Author had written this to reduce the redundancy of redeclaration of the type parameters as
Box<String> box=new Box<String>();
So, he used the factory method like above.
Box<String> myBox = BoxImpl.make();
But here my doubt is why <V> in the make method of the Box class, why can't we use like below:
public class BoxImpl<T> implements Box<T> {
public static<T> Box<T> make() {
return new BoxImpl<T>();
}
What is the difference between above two ?
This technique (used, among the others, by the Guava collections) was quite useful before the introduction of the diamond operator <> in Java 7, to avoid very noisy code like:
// Java 5:
Box<Map<String, List<Integer>> box = new BoxImpl<Map<String, List<Integer>>();
// with static inference
Box<Map<String, List<Integer>> box = BoxImpl.make();
// with diamond operator - Java7
Box<Map<String, List<Integer>> box = new BoxImpl<>();
As per why declaring a new type parameter V in the make method, it's because it's static, and static method can't access type arguments because they are out of scope, and belong to a specific instantiation of a type (similar to instance variables, that can't be used in static methods)
This
public class BoxImpl<T> implements Box<T> {
is a generic class declaration. It declares a type variable T for use in instance class members.
This
public static<V> Box<V> make() {
return new BoxImpl<V>();
}
is a generic method. It declares its own generic variables and is static. It cannot use the class type variables.
The name of the type variable is not an issue here. The name could be the same, but it will not be referring to the same type.
Making that method generic is important, because you can't use the type parameter T of the class in a static context. So the following code wouldn't compile:
class BoxImpl<T> implements Box<T> {
// Will fail to compile. Cannot use type parameter `T` in static context
public static Box<T> make() {
return new BoxImpl<T>();
}
}
From Java Generics FAQs:
The scope of a class's type parameter is the entire definition of the
class, except any static members or static initializers of the class.
This means that the type parameters cannot be used in the declaration
of static fields or methods or in static nested types or static
initializers.
Related
I'm testing code with reflections and I have a problem with reproduce one if statement in my code, probably because I don't understand correctly how the method works.
class TestClass {
public Collection<TestField> field;
}
...
ParameterizedType type = (ParameterizedType) TestClass.class.getField("field").getGenericType();
Now, I am looking for a FieldType, for which the getActualTypeArguments() method will return an EMPTY Type[] array.
Based on documentation:
Note that in some cases, the returned array be empty. This can occur if this type represents a non-parameterized type nested within a parameterized type.
Unfortunately, I have a problem with understanding this fragment, so I can't design a class that will fulfill it.
Section 4.5 of the JLS has a footnote about this:
A parameterized type may be an parameterization of a generic class or interface which is nested. For example, if a non-generic class C has a generic member class D<T>, then C.D<Object> is a parameterized type. And if a generic class C<T> has a non-generic member class D, then the member type C<String>.D is a parameterized type, even though the class D is not generic.
So C<String>.D is also a ParameterizedType! This is what the documentation of getActualTypeArguments was referring to. And according to the docs, getActualTypeArguments would return an empty array for C<String>.D. Here is an example:
class Foo<T> {
class Bar {
}
}
public class Main {
public Foo<Integer>.Bar bar;
public static void main(String[] args) throws Exception {
ParameterizedType type = (ParameterizedType) Main.class.getField("bar").getGenericType();
// this prints 0
System.out.println(type.getActualTypeArguments().length);
}
}
I am attempting to look up a type parameter at runtime using TypeToken as showing in the Guava documentation example IKnowMyType:
public class Test<E extends Enum<E>> {
private static enum MyEnum {
FIRST,
SECOND
};
private final TypeToken<E> enumType = new TypeToken<E>(getClass()) {
};
public static void main(String[] args) {
Test<MyEnum> container = new Test<>();
System.out.println(container.enumType.getRawType());
}
}
When I run this code, I get class java.lang.Enum as output. Why am not not getting class MyEnum instead?
This "hack" won't work on a value of runtime type Test.
There's no way for Java to propagate the type argument inferred when instantiating your Test class here
Test<MyEnum> container = new Test<>();
down to the declaration
private final TypeToken<E> enumType = new TypeToken<E>(getClass()) {
};
And therefore the TypeToken has no idea what the E should refer to.
The Javadoc states
Constructs a new type token of T while resolving free type variables
in the context of declaringClass.
Clients create an empty anonymous subclass. Doing so embeds the type
parameter in the anonymous class's type hierarchy so we can
reconstitute it at runtime despite erasure.
So that's what you need to do.
Test<MyEnum> container = new Test<MyEnum>() {
};
Now, because classes maintain information about their generic superclasses, the getClass call in the TypeToken instantiation above provides enough context for the E to be interpreted as MyEnum.
Given
public class Foo {
public static class FooBuilder { ... }
}
I want to write a method on a third class that returns Foo, given Foo.FooBuilder.class
i.e.
Foo f = x.make(Foo.FooBuilder.class, someData);
Is it possible to declare a signature using generics that can imply the return type? Is there some language feature that lets me say "type U is outer class of type T"?
Obviously, it is possible to specify that type extends, or is the base of, a generic type (U extends T or U super T, respectively) but I am looking for U outer T which is, I think, more than Java can offer, even indirectly, at least in 1.7, which I am targeting.
So far, I have simply declared both inner and outer types, which works but is a wider definition than I am after and looks clumsy too.
public <TYPE,BUILDER> TYPE make(Class<BUILDER> builderClass, Map<String,Object> data) {
// Construct TYPE
}
Is there a way to infer TYPE without explicitly providing a template parameter?
There is a Class#getDeclaringClass method that may work in your case.
Quoting the docs:
If the class or interface represented by this Class object is a member of another class, returns the Class object representing the class in which it was declared.
EDIT:
After the clarification of OP, here is the new suggestion:
You create an generic interface to mark all your nested classes:
public interface Nested<P> {
}
Then you apply it to your Foo.Bar class like this:
public class Foo {
public static class Bar implements Nested<Foo> {
}
}
Then in your factory you can have the following:
public <P> P make(Class<? extends Nested<P>> clazz, Map<String, Object> someData) {
// do whatever you need to do
return (P) clazz.getDeclaringClass();
}
However, with this construct, there is not way to validate it your nested class is the real class, declared when implementing the generic interface.
I'm writing generic class
public class SomeClass<T> {
public static <T extends Comparable<? super T>> T min(Collection<? extends T> c) {
T min = c.iterator().next();
for (T element : c)
if (element.compareTo(min) < 0)
min = element;
return min;
}
}
public class Main {
public static void main(String[] args) {
SomeClass<Integer>.min(Arrays.asList(1, 2, 3)); // compile-time error
SomeClass.min(Arrays.asList(1, 2, 3)); // ok
}
}
In generic class SomeClass and generic method SomeMethod type-parameter T is the same or defference?
Why we have compile time-error on the string SomeClass<Integer>.min(Arrays.asList(1,2,3));?
The class declaration
public class SomeClass<T>
defines a generic class, where <T> specifies the type parameter (also called type variable). This introduces the type variable, T, that can be used anywhere inside the class.
And the method declaration:
public static <T extends Comparable<? super T>> T min(Collection<? extends T> c) {
...
}
defines a generic method. Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared.
Now if you want to call the generic method min, you need to call:
SomeClass.<Integer>min(Arrays.asList(1,2,3));
It's different, and you should never write code like this, specifically because of the possibility for confusion. Always use different type variables on classes and methods inside those classes.
T is for Type and you are accessing static method which has nothing to do with T.
Either use like this
SomeClass<Integer> a = new SomeClass<Integer>();
a.min(Arrays.asList(1, 2, 3));
OR
SomeClass.min(Arrays.asList(1, 2, 3));
The two Ts are different: the fist one is a parameter for the class (and is unused) and the second one is specific to the method. Since the method is static, the class parameter doesn’t affect it.
When you write SomeClass<Integer>.min(Arrays.asList(1, 2, 3));, you get an error because it doesn’t make sense to add a parameter to SomeClass since no object of that class is instanciated. SomeClass is only used to tell the compiler that you want to call a static method from that class. You can add a parameter to the method with SomeClass.<Integer>min(Arrays.asList(1, 2, 3));, but you don't have to since the compiler can infer the type here.
So what the compile message told you was that it's a syntax error. Why it's invalid syntax is easy to understand. You are trying to call a method on a type. That means you are calling a static method. Static methods are not within the scope of generic type parameters. So it is not allowed to put a generic type argument on the left side.
If you want the technical reason, it's because your method call expression is not one of the forms that's allowed by the syntax. The closest form to yours is TypeName . NonWildTypeArguments Identifier ( ArgumentListopt ). But TypeName (which is defined here) must be an identifier or package-qualified identifier. It does not allow brackets.
I've read posts about why you can't have a (Edit -- generic) (which use that type parameter from the generic class) static method in a generic class, but why can you then use static generic methods in non generic classes? I don't see the why the second is allowed, but I kinda understand why the first isn't.
why you can't have a (Edit -- generic) (which use that type parameter from the generic class) static method in a generic class
The reason for this is simple: The type parameter is not associated with the class but with instances of the class.
I.e., you can't do
class Test<T> {
public static void sayHello(T t) { // T for which instance?!
System.out.println("Hello");
}
}
why can you then use static generic methods in non generic classes?
Why wouldn't you? A generic method takes the type parameter, so it doesn't matter if it's static or not, or if the class it's in is generic or not etc.
This for instance compiles fine:
class Test {
public static <T> void sayHello(T t) {
System.out.println("Hello " + t);
}
}
And you'd call the method like this:
Test.<String>sayHello("some argument");
^^^^^^^^
type parameter provided at the method-call: no instance required.