interface Parent {
public String name(Object b);
}
class Child implements Parent {
public String name(Object b) {
return [???];
}
public String name(String b) {
return "Child " + [???];
}
}
public class Exercise {
public static void main(String[] args) {
Parent p = new Child();
assert p.name("String").equals("Child String");
}
}
Is there any way to replace the '[???]' within the code above so that the assert returns trues?
I don't understand why the method name is overloaded. But you could use instanceof to check for the type of the parameter b.
public String name(Object b) {
if (b instanceof String) {
return name((String) b);
}
return "something else";
}
public String name(String b) {
return "Child " + b;
}
Related
I have a problem with method override checks. I can detect simple override relations, but if the parent class has generics and the abstract method uses type parameters (return value/args), my code breaks down because the method description is not equal to the checked method.
Example:
public interface ISetting<T> {
public T method();
}
public class Setting implements ISetting<Integer> {
public Integer method() {
//Something
}
}
In ISetting, the method description is ()Ljava/lang/Object;
and in Setting, the method description is ()Ljava/lang/Integer;
How I can check this Override ?
On my head no thoughts come, how I can make this >~< All ideas which come to my head are bad (example: ignore check on desc, but overload method just break this idea)
Note that your issue does not only apply to generic supertype. You can also override a method with a more specific return type, with no Generics involved, e.g.
interface SomeInterface {
Object method();
}
class SomeImplementation implements SomeInterface {
#Override
public Integer method() {
return null;
}
}
You have to understand the concept of bridge methods.
A bridge method performs the task of overriding a method on the byte code level, having exactly the same parameter types and return type as the overridden method, and delegates to the actual implementation method.
Since the bridge method only consists of this invocation instruction, some type casts if required, and the return instruction, it is easy to parse such a method to find the actual method it belongs to, without dealing with the complex rules of the Generic type system.
Using, the following helper classes
record MethodSignature(String name, String desc) {}
record MethodInfo(int access, String owner, String name, String desc) {
MethodSignature signature() {
return new MethodSignature(name, desc);
}
}
final class MethodAndBridges {
MethodInfo actual;
final List<MethodInfo> bridges = new ArrayList<>();
MethodAndBridges(MethodSignature sig) {}
void set(MethodInfo mi) {
if(actual != null) throw new IllegalStateException();
actual = mi;
}
void addBridge(MethodInfo mi) {
bridges.add(mi);
}
}
We can gather the information in a form ready for checking override relations with the ASM library as follows:
class MethodCollector extends ClassVisitor {
static Map<MethodSignature, MethodAndBridges> getMethods(ClassReader cr) {
MethodCollector mc = new MethodCollector();
cr.accept(mc, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return mc.found;
}
final Map<MethodSignature, MethodAndBridges> found = new HashMap<>();
String owner, superClass;
List<String> interfaces;
protected MethodCollector() {
super(Opcodes.ASM9);
}
#Override
public void visit(int version, int acc,
String name, String sig, String superName, String[] ifNames) {
owner = name;
superClass = superName;
this.interfaces = ifNames == null? List.of(): List.of(ifNames);
}
#Override
public MethodVisitor visitMethod(
int acc, String name, String desc, String sig, String[] exceptions) {
MethodInfo mi = new MethodInfo(acc, owner, name, desc);
if((acc & Opcodes.ACC_BRIDGE) == 0) {
found.computeIfAbsent(mi.signature(), MethodAndBridges::new).set(mi);
return null;
}
return new MethodVisitor(Opcodes.ASM9) {
#Override public void visitMethodInsn(
int op, String owner, String name, String tDesc, boolean i) {
found.computeIfAbsent(new MethodSignature(name, tDesc),
MethodAndBridges::new).addBridge(mi);
}
};
}
}
To demonstrate how this work, let’s enhance your example, to address more cases
interface SupplierOfSerializable {
Serializable get();
}
interface ISetting<T extends CharSequence> extends Supplier<T>, Consumer<T> {
T get();
#Override void accept(T t);
Number method(int i);
static void method(Object o) {}
private void method(Number n) {}
}
class Setting implements ISetting<String>, SupplierOfSerializable {
public String get() {
return "";
}
#Override
public void accept(String t) {}
public Integer method(int i) {
return i;
}
static void method(Object o) {}
void method(Number n) {}
}
and check the override relations (only considering the direct interfaces, without recursion)
public class CheckOverride {
public static void main(String[] args) throws IOException {
MethodCollector mc = new MethodCollector();
new ClassReader(Setting.class.getName())
.accept(mc, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
Map<MethodSignature, MethodAndBridges> implMethods = mc.found;
Map<MethodInfo, Set<MethodInfo>> overrides = new HashMap<>();
for(String ifType: mc.interfaces) {
Map<MethodSignature, MethodAndBridges> ifMethods
= MethodCollector.getMethods(new ClassReader(ifType));
System.out.println("interface " + ifType.replace('/', '.'));
printMethods(ifMethods);
System.out.println();
ifMethods.values().removeIf(CheckOverride::nonOverridable);
implMethods.forEach((sig, method) -> {
if(nonOverridable(method)) {
overrides.putIfAbsent(method.actual, Set.of());
return;
}
var overridden = ifMethods.get(sig);
if(overridden == null && method.bridges.isEmpty()) {
overrides.putIfAbsent(method.actual, Set.of());
return;
}
Set<MethodInfo> set = overrides.compute(method.actual,
(k, s) -> s == null || s.isEmpty()? new HashSet<>(): s);
if(overridden != null) set.add(overridden.actual);
for(var mi: method.bridges) {
overridden = ifMethods.get(mi.signature());
if(overridden != null) set.add(overridden.actual);
}
});
}
System.out.println("class " + mc.owner.replace('/', '.'));
printMethods(implMethods);
System.out.println();
System.out.println("Final result");
System.out.println("class " + mc.owner.replace('/', '.'));
overrides.forEach((m,overridden) -> {
System.out.println(" " + toDeclaration(m, false));
if(!overridden.isEmpty()) {
System.out.println(" overrides");
overridden.forEach(o ->
System.out.println(" " + toDeclaration(o, true)));
}
});
}
static boolean nonOverridable(MethodAndBridges m) {
return (m.actual.access() & (Opcodes.ACC_PRIVATE|Opcodes.ACC_STATIC)) != 0
|| m.actual.name().startsWith("<");
}
static void printMethods(Map<MethodSignature, MethodAndBridges> methods) {
methods.forEach((sig, methodAndBridges) -> {
System.out.println(" "+toDeclaration(methodAndBridges.actual,false));
if(!methodAndBridges.bridges.isEmpty()) {
System.out.println(" bridges");
for(MethodInfo mi: methodAndBridges.bridges) {
System.out.println(" " + toDeclaration(mi, false));
}
};
});
}
private static String toDeclaration(MethodInfo mi, boolean withType) {
StringBuilder sb = new StringBuilder();
sb.append(Modifier.toString(mi.access() & Modifier.methodModifiers()));
if(sb.length() > 0) sb.append(' ');
String clName = mi.owner();
var mt = MethodTypeDesc.ofDescriptor(mi.desc());
if(mi.name().equals("<init>"))
sb.append(clName, clName.lastIndexOf('/') + 1, clName.length());
else {
sb.append(mt.returnType().displayName()).append(' ');
if(withType) sb.append(clName.replace('/', '.')).append('.');
sb.append(mi.name());
}
if(mt.parameterCount() == 0) sb.append("()");
else {
String sep = "(";
for(ClassDesc cd: mt.parameterList()) {
sb.append(sep).append(cd.displayName());
sep = ", ";
}
sb.append(')');
}
return sb.toString();
}
}
interface ISetting
public static void method(Object)
public abstract void accept(CharSequence)
bridges
public void accept(Object)
public abstract Number method(int)
private void method(Number)
public abstract CharSequence get()
bridges
public Object get()
interface SupplierOfSerializable
public abstract Serializable get()
class Setting
Setting()
public Integer method(int)
bridges
public Number method(int)
public void accept(String)
bridges
public void accept(Object)
public void accept(CharSequence)
static void method(Object)
public String get()
bridges
public Object get()
public CharSequence get()
public Serializable get()
void method(Number)
Final result
class Setting
public String get()
overrides
public abstract Serializable SupplierOfSerializable.get()
public abstract CharSequence ISetting.get()
Setting()
public Integer method(int)
overrides
public abstract Number ISetting.method(int)
public void accept(String)
overrides
public abstract void ISetting.accept(CharSequence)
void method(Number)
static void method(Object)
The code uses newer Java features, like var, record, and the constant API, but I think, the result is straight-forward enough for converting it to older Java versions, if really required.
I have two types of objects that can perform a calculate() operation with either an int or a byte:
public class A {
public int calculate(int n) {...}
}
and
public class B {
public byte calculate(byte n) {...}
}
I want to have an ArrayList of objects that I can loop over calling the calculate method.
How do I do that, using an interface?
Considering the difference in the int/byte signature
Would something like this be a good approach?
public interface Calculatable {
int calculate(int number);
default byte calculate(byte number) {
return (byte) calculate((int) number);
}
}
Maybe using a 3-rd class and check based on type could be useful
public class TestCalc {
public static void main(String[] args) {
List<Object> l = new ArrayList<Object>();
l.add(Integer.valueOf(300));
l.add(Byte.valueOf("120"));
l.add(Integer.valueOf(1));
TestCalc tc = new TestCalc();
ComputeAB cab = tc.new ComputeAB();
for (Object o : l) {
System.out.println(cab.calculate(o) + ":" + cab.type);
}
}
class A {
public int calculate(int n) {
return n;
}
}
class B {
public byte calculate(byte n) {
return n;
}
}
class ComputeAB {
String type = "";
public Object calculate(Object o) {
if (o instanceof Integer) {
type = "int";
return new A().calculate((int) o);
}
type = "byte";
return new B().calculate((byte) o);
}
}
}
Output
300:int
120:byte
1:int
String a = "1";
String b;
...
String n = "100";
How can I check if none or all of the properties have been set?
I want to get "valid" if a..n all properties are set, and also "valid" if none if them are set. But "invalid" if only partially set.
How can this be solved? Of course I could write endless boolean statements like
(a != null && b != null & .. & n != null) || (a == null && b == null & .. & n == null)
But there must be a better way.
Having a sample class
public class SampleClass {
private String a, b, c, d, e, f;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
public String getE() {
return e;
}
public void setE(String e) {
this.e = e;
}
public String getF() {
return f;
}
public void setF(String f) {
this.f = f;
}
}
you can get the Java Bean information using the java.beans.Introspector
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import org.junit.Test;
public class IntrospectorTest {
#Test
public void test() throws IntrospectionException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
SampleClass sampleClass = new SampleClass();
sampleClass.setA("value for a");
sampleClass.setB("value for b");
sampleClass.setC("value for c");
sampleClass.setD("value for d");
sampleClass.setE("value for e");
sampleClass.setF("value for f");
int withValue = 0;
PropertyDescriptor[] descriptors = Introspector.getBeanInfo(SampleClass.class, Object.class).getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : descriptors) {
Object value = new PropertyDescriptor(propertyDescriptor.getName(), SampleClass.class).getReadMethod().invoke(sampleClass);
if (value!=null) {
withValue++;
System.out.println(propertyDescriptor.getName() + ": " + value);
}
}
if (descriptors.length == withValue || withValue == 0) {
System.out.println("valid");
}else{
System.err.println("Invalid!!");
}
}
}
and voila!
Pay atention at this line
Introspector.getBeanInfo(SampleClass.class, Object.class).getPropertyDescriptors();
if you call the getBeanInfo method with your class as one and only parameter the Introspector will return all the Property Descriptors in the class hierarchy, so you can call the method with an optional stop class where the Introspector stops reading the Property Descriptors.
Hope this helps.
You can use map then iterate over it to check if any of the value is null and set status accordingly.
You can also try with this: Collections.frequency(map.values(), null) == map.size()
I have immutable class and want add new constructor without duplicating code in both constructors.
I have class:
public class Test {
private final String stringParameter;
public Test() {
stringParameter = "firstReallyLongDefaultString";
}
public Test(String s) {
stringParameter = s;
}
}
And I want to add the new constructor with "char" parameter, something like this:
public Test(char s) {
if(Character.isLetter(s)) {
stringParameter = "firstReallyLong" + s + "DefaultString";
} else {
stringParameter = "firstReallyLongDefaultString";
}
}
How can I do it without the code repetition of the long string? I would like to call "this()" constructor in else branch but it's not possible.
public Test(char s) {
this(Character.isLetter(s) ? "firstReallyLong" + s + "DefaultString" : "firstReallyLongDefaultString");
}
You also could chain them more explicitly, removing some code repetition:
public class Test {
private static final String DEFAULT_VALUE = "firstReallyLongDefaultString";
private final String stringParameter;
public Test() {
this(DEFAULT_VALUE);
}
public Test(String s) {
stringParameter = s;
}
public Test(char c) {
this(prepareString(c));
}
private static String prepareString(char c) {
if(Character.isLetter(s)) {
return "firstReallyLong" + s + "DefaultString";
} else {
return DEFAULT_VALUE;
}
}
}
The "firstReallyLongDefaultString" better to be done as a private constant to avoid repetition.
Like this:
public Test(char s) {
super();
if(Character.isLetter(s)) {
stringParameter = "firstReallyLong" + s + "DefaultString";
}
}
A factory method gives you more flexibility:
public static Test create(char c) {
final String parameter;
if(Character.isLetter(s)) {
parameter = "firstReallyLong" + s + "DefaultString";
} else {
parameter = "firstReallyLongDefaultString";
}
return new Test(parameter);
}
This can't be inherited in subclasses, however if you want your class to be strictly immutable it should be final.
Here is some of my Java code
public List<OBJ> a = new ArrayList<OBJ>();
public String A;
public String B;
public String C;
for (OBJ o : a) {
// .... TODO
}
So I have an interface OBJ and there are three objects that implements OBJ say X, Y, Z. So I store X/Y/Z objects in List a. Now say that I want to go through the loop and if o is of instance X store X.value in A, if Y store Y.value in B, and if Z store Z.value in C. So the problem is really how do you figure out what object type o is (X,Y,Z) to store their values in the right string.
NOTE: I want to use the Visitor pattern or something like it, but I don't really have a firm grasp of it so I'm asking for your help.
This means NO Instanceof(s) or Type Casts and NO Dedicated Methods like
interface OBJ {
void blah();
}
class X implements OBJ {
public void blah();
} // etc
Thanks! I really want to get this import aspect of software engineering down!
Hey wow thanks for the detailed and fast responses, but my situation is a bit more complicated and sorry I didn't add this before.
So String A, B, C are actually housed in another class like
class ARGH {
public List<OBJ> a = new ArrayList<OBJ>();
public String A;
public String B;
public String C;
//invisible constructor here
public String toString () {
for (OBJ o : a) {
// .... TODO
}
return "some string"
}
}
public void main (String[] args) {
ARGH argh = new ARGH();
// Setup some X, Y, Z objects and their values here
String D = argh.toString();
// Do something to D
}
So the Strings and List are actually not global variables so I don't think this would work:
ObjVisitor v = new ObjVisitor() {
#Override
public void visit(X x) {
A = x.value();
}
// And so on.
}
I am assuming I have to somehow pass in the String A, B, C into the visit method but I don't know how to do that and still stay with The Visitor Pattern.
In a nut-shell you'd do like this:
Create a Visitor interface ObjVisitor with one visit-method for each type.
Add an abstract accept(ObjVisitor v) to OBJ.
Add an accept(ObjVisitor v) { v.visit(this); } to each OBJ implementation.
Call o.accept(yourVisitorImpl) in the loop.
You did indeed get some code from Bringer128. I elaborated a bit and added the String-stuff.
import java.util.*;
interface OBJ {
String accept(ObjVisitor v);
}
interface ObjVisitor {
String visit(X x);
String visit(Y y);
String visit(Z z);
}
class X implements OBJ {
public String accept(ObjVisitor v){ return v.visit(this); }
}
class Y implements OBJ {
public String accept(ObjVisitor v) { return v.visit(this); }
}
class Z implements OBJ {
public String accept(ObjVisitor v) { return v.visit(this); }
}
Usage:
class Test {
public static void main(String[] args) {
List<OBJ> objs = Arrays.asList(new Z(), new X());
ObjVisitor toStringVisitor = new ObjVisitor() {
public String visit(X x) { return "X object"; }
public String visit(Y y) { return "Y object"; }
public String visit(Z z) { return "Z object"; }
};
String result = "";
for (OBJ o : objs)
result += o.accept(toStringVisitor) + "\n";
System.out.println(result);
// Prints
// Z object
// X object
}
}
An alternative (perhaps better approach) would be to let the visitor implementation maintain a StringBuilder, let the visit-methods return void, and after the loop just call stringVisitor.getCurrentString() or something like that.
aioobe spelled it out for you, but here is what the implementation would look like.
interface OBJ {
void blah();
// New method!
void accept(ObjVisitor v);
}
class X implements OBJ {
public void blah() {}
#Override
public void accept(ObjVisitor v) {
v.visit(this);
}
}
interface ObjVisitor {
public void visit(X x);
public void visit(Y y);
}
Now use it:
public List<OBJ> a = new ArrayList<OBJ>();
public String A;
public String B;
public String C;
public void myMethod() {
ObjVisitor v = new ObjVisitor() {
#Override
public void visit(X x) {
A = x.value();
}
// And so on.
}
for (OBJ o : a) {
o.accept(v);
}
}