Why does IntelliJ sometimes throw an unused warning but sometimes not? - java

I have many Java classes with two constructors:
a private constructor with no arguments and with unused warning suppression called by gson;
a public constructor with arguments.
In one class IntelliJ informs me with a warning that the public constructor is never used. In all other classes there isn't the warning, but if I click Find Usage for the method it displays "Nothing found in project files".
Why is there a warning in some cases and not in others? How can I make IntelliJ always behave in the same way?
This is the class where the public constructor throws the warning:
public class ShopMarketAction extends Action {
private Boolean inRow = null;
private Integer index = null;
#SuppressWarnings("unused") // Called by gson
private ShopMarketAction() {
super(ActionType.SHOP_MARKET);
}
// THIS METHOD THROWS AN UNUSED WARNING
public ShopMarketAction(boolean inRow, int index) {
super(ActionType.SHOP_MARKET);
this.inRow = inRow;
this.index = index;
}
}
This is an example class where the public constructor doesn't throw the warning:
public class ProductionAction extends Action {
private Integer cardIndex = null;
#SuppressWarnings("unused") // Called by gson
private ProductionAction() {
super(ActionType.PRODUCE);
}
// THIS METHOD DOESN'T THROW THE WARNING BUT IS NEVER USED
public ProductionAction(int cardIndex) {
super(ActionType.PRODUCE);
this.cardIndex = cardIndex;
}
}
I specify that in both classes the method is not used yet.
The Action class that both classes extends:
public abstract class Action {
#SuppressWarnings({"unused", "FieldCanBeLocal"})
private final ActionType type; // Used by gson
protected Action(ActionType type) {
this.type = type;
}
}
The ActionType enum:
public enum ActionType {
SHOP_MARKET, PRODUCE;
}

I found what causes the problem: I have a non-Java file (an UXF file created by another application) which contains the name of all these methods and for some reason IntelliJ consider them as used because of this.

Related

Java Require "this" in the constructor of the inheriting class

I have the following classes: Command, ParameterData, and TestCommand. Command is an abstract class that represents a simple object. This class requires a list of ParameterData objects. ParameterData, in turn, also requires an instance of the Command class in its constructor. I wanted to create a class inheriting from Command, i.e. TestCommand. Here's the problem: when invoking the constructor, I get a compile error: "Cannot reference 'this' before supertype constructor has been called". I don't know how to fix this problem. I will be grateful for your help.
Command class:
public abstract class Command {
private final String SETTINGS_PATH;
private final List<ParameterData> PARAMETERS;
public Command(String settingsPath, List<ParameterData> parameters) {
this.SETTINGS_PATH = settingsPath;
this.PARAMETERS = parameters;
}
public String getSettingsPath() {
return SETTINGS_PATH;
}
public abstract void run();
}
ParameterData class:
public class ParameterData {
private final String SETTINGS_KEY;
private final Command COMMAND;
private final OptionType OPTION_TYPE;
private final boolean REQUIRED;
public ParameterData(String settingsKey, Command command, OptionType optionType, boolean required) {
this.SETTINGS_KEY = settingsKey;
this.COMMAND = command;
this.OPTION_TYPE = optionType;
this.REQUIRED = required;
}
public String getSettingsKey() {
return SETTINGS_KEY;
}
public String getSettingsPath() {
return COMMAND.getSettingsPath() + ".Parameters." + SETTINGS_KEY;
}
public OptionType getOptionType() {
return OPTION_TYPE;
}
public boolean isRequired() {
return REQUIRED;
}
}
TestCommand class (error occurs with "this"):
public class TestCommand extends Command {
public TestCommand() {
super("Settings.TestCommand",
List.of(new ParameterData("SettingsKey", this, OptionType.STRING, true)));
}
#Override
public void run() {
//do something
}
}
I don't know how to fix this problem.
It cannot be fixed. You can't hand an instance of this around when your this reference isn't initialized yet. Think about it, it's a chicken and egg problem: That this reference has all sorts of crazy stuff going on. It'll have final fields that aren't initialized yet, i.e. final fields whose value will be changing if you query it.
Within the chain of constructors, thems the breaks. But you're not allowed to aggravate this problem by sending this to other places when this isn't "ready yet". Constructors are part of the 'birth' of an object and this refers to the baby. You can't hand your baby to others to coo at when it's not (fully) born yet.
If you want 2 objects that refer to each other, both with final fields? Not possible.
Make one field non-final. Use a builder system and make the 'setters' for this non-final field package private or fully private and whilst the field isn't final, your object will still be immutable for all intents and purposes - it cannot be observed to change once it escapes its package.

Generically providing a setter for a Decorated object that is stored in an array

I'm probably going about this in the most complicated way, but I'm hoping what I'm trying to do makes sense here.
Suppose I have some set of unrelated, generated classes and I want to Decorate them to create some kind of common API. So something like:
public abstract class GeneratedDecorator<T> {
private T generated;
public T getGenerated() { return generated; }
public void setGenerated(T generated) { this.generated = generated; }
public abstract String getString();
public static class ClassA extends GeneratedDecorator<GeneratedClassA> {
#Override
public String getString() { return getGenerated().getThisString(); }
}
public static class ClassB extends GeneratedDecorator<GeneratedClassB> {
#Override
public String getString() { return getGenerated().getADifferentString(); }
}
}
Now, to use this new fancy class I just say:
GeneratedDecorator.ClassA a = new GeneratedDecorator.ClassA();
a.setGenerated(myGeneratedInstanceA);
a.getString();
Ok so far so-so ... but now I want to manage an array of these Decorators.
So let's try:
public abstract class DecoratorBundle<T extends GeneratedDecorator> {
private static final int MAX_ROWS = 10;
private T[] bundle;
DecoratorBundle() { bundle = createBundle(); }
public String getString(int index) { return bundle[index].getString(); }
public void setRow(??? generated, int index ) {
//check index of bundle, if null create a new instance of appropriate type and set bundle[index] = new instance
//call setGenerated on instance at index
}
protected abstract T[] createBundle();
public static class ClassA extends DecoratorBundle<GeneratedDecorator.ClassA> {
#Override
protected GeneratedDecorator.ClassA[] createBundle() {
return new GeneratedDecorator.ClassA[MAX_ROWS];
}
}
public static class ClassB extends DecoratorBundle<GeneratedDecorator.ClassB> {
#Override
protected GeneratedDecorator.ClassB[] createBundle() {
return new GeneratedDecorator.ClassB[MAX_ROWS];
}
}
}
Here's where I'm stuck ... I want this DecoratorBundle to have a setRow(??? generated, int index) where the parameter is of the GeneratedDecorator's type (i.e, GeneratedClassA or GeneratedClassB). Seems like type erasure will probably make this impossible, but it would be really nice to have this DecoratorBundle class to completely manage it's bundle array. It currently is able to instantiate the array, but I want some way for it to create a new GeneratedDecorator-type and assign it in a setRow method.
If I'm going about this completely wrong then I would love to hear another idea.

How to reintegrate anonymous classes into Java code produced by the ECD decompiler?

I have a jar for school that is supposed to be decompiled, modifed, and reevaluated. I decompiled all of the class files using the ECD plugin for Eclipse, but I think I have a few anonymous classes that were extracted and need to be merged back into another class. I have a class P, and then five more classes named P$1, P$2, ..., P$5.
Here's the problem parts of P:
public class P {
private ArrayList<Family> group;
private int marker;
private Integer primaryElement;
Comparator<Family> c;
public P(ArrayList<Family> g, Integer i, Comparator<Family> c) {
this.marker = -1;
this.group = new ArrayList(g);
this.primaryElement = i;
this.c = c;
}
/* Some unrelated methods */
public String printHeader() {
return this.print(new 1(this));
}
public String printRow(Integer i) {
return this.print(new 2(this, i));
}
public String printPad() {
return this.print(new 3(this));
}
public Object printCost() {
return this.print(new 4(this));
}
public String printLine() {
return this.print(new 5(this));
}
Here is P$1. The others are very similar.
final class P$1 implements PrintCommand {
P$1(P arg0) {
this.this$0 = arg0;
}
public String print(Family f) {
return String.format("%3d", new Object[]{Integer.valueOf(f.getId())});
}
}
In case you're wondering, PrintCommand is a super simple interface:
public interface PrintCommand {
String print(Family arg0);
}
How can I get P$1 merged back into P? Also, what does this.this$0 mean in P$1?
In an anonymous class you can reference the this from the enclosing class with P.this. To do that, the java compiler will create a constructor, which will set a field named this$0 to the reference passed to the constructor.
The original code probably looked like this:
public String printHeader() {
return this.print(new PrintCommand() {
public String print(Family f) {
return String.format(%3d", f.getId());
}
);
}
There are other things the compiler does, for example adding accessor methods for private methods/fields from the enclosing class that are accessed in the inner class. Or passing the value of (effectively) final variables used in the inner class to the constructor.
From the perspective of the Java Runtime, there is no anonymous inner class, only named classes.

method() in x.y.z is defined in an inaccessible class or interface

Super.java
package x.y.z;
public abstract class Super {
public CustomClass a() {
return new CustomClass();
}
public abstract String getName();
public abstract String getDescription();
}
Sub.java
package x.y.z;
public abstract class Sub extends Super {
public String getDescription() {
return "Is a Sub";
}
}
User.java
package x.y.z;
public class User {
private class UseCase extends Sub {
public String getName() {
return "UseCase";
}
}
public UseCase use() {
return new UseCase();
}
}
In another part of my app I try to access new User().use().a(), and I think this causes the error (it's a compile-time error though).
Trying to compile the above errors:
a() in x.y.z.Super is defined in an inaccessible class or interface
What's causing this error and how do I fix it?
New question
This makes the error disappear for me:
User.java
package x.y.z;
public class User {
private class UseCase extends Sub {
public String getName() {
return "UseCase";
}
}
public Super use() {
return new UseCase();
}
}
Changing the type of User.use() to Super "fixes" the error.
Is this a problematic "fix", or will this work fine without any hiccups?
a() in x.y.z.Super is defined in an inaccessible class or interface
Because a UseCase class, that you are trying to return, is private. Consider the following example, that compiles without errors:
User user = new User();
Sub sub = user.use(); // because of implicit up-casting to Sub (and Sub is public)
sub.a();
If you want to fit these 3 lines into a single expression, you need an explicit cast:
CustomClass custom = ((Sub) new User().use()).a();
Also, as you've already pointed out, you can change the use(...) method return type to a Sub or a Super class, so the following code will work without additional casts:
CustomClass custom = new User().use().a();
The problem here is that the type that you return from User#use (i.e. UseCase) is private to the User class, prohibiting it from being accessed from anywhere outside User. Modifying UseCase to be public instead of private should fix your issue.
public class UseCase extends Sub {
#Override
public String getName() {
return "UseCase";
}
}

Inheritance and static factory methods

I have programmed a java.util.logging.Formatter (named OneLineFormatter) for my console outputs. It has two static factory methods, both call the private constructor.
Now I wanted to program a second one for debugging purposes (named DebugFormatter), which only overrides the method formatRecord in OneLineFormatter so the traces are printed too instead of just the localized message and the class.
Eclipse warned me that the super constructor OneLineFormatter() is undefined and I have to invoke another constructor. I googled the problem and found this: Java error: Implicit super constructor is undefined for default constructor on StackOverflow.
But I do not want to create a public constructor because that would be against the factory principle. The factory methods and the constructor can be the same (the DebugFormatter factory methods should create a new DebugFormatter instead of a OneLineFormatter though).
If you need some more info just ask. Thanks for your help in advance!
The code:
public class OneLineFormatter extends Formatter {
public static Formatter withPackageFromRoot(String rootName) {
return new OneLineFormatter(rootName);
}
public static Formatter withClassOutputOnly() {
return new OneLineFormatter("");
}
private String rootName;
private OneLineFormatter(String rootName) {
this.rootName = rootName;
}
#Override
public String format(LogRecord record){<code>}
private String formatRecord(LogRecord record{<code that I want to override>}
}
And the second class:
public class DebugFormatter extends OneLineFormatter {
public static Formatter withClassOutputOnly() {
return new DebugFormatter("");
}
public static Formatter withPackageFromRoot(String rootName) {
return new DebugFormatter(rootName);
}
private DebugFormatter(String rootName) {<same as OneLineFormatter(String)>}
#Override
private String formatRecord(LogRecord record) {<code>}
}
EDIT 1: added code
EDIT 2: corrected code
You could just make the constructor for OneLineFormatter package-private or protected. This way you could reduce the access to the constructor to a point that fits your needs
OneLineFormatter(String rootName) {
this.rootName = rootName;
}
// OR
protected OneLineFormatter(String rootName) {
this.rootName = rootName;
}

Categories