0

Example from another stackoverflow thread link:

public class RegularEmployee {
    private BigDecimal salary;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }

    public BigDecimal calculateBonus() {
        return salary.multiply(getBonusMultiplier());
    }

    /* ... presumably lots of other code ... */
}

public class SpecialEmployee extends RegularEmployee {
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

This doesn't behave as some people might think, because calculateBonus always takes the getBonusMultiplier from RegularEmployee, even if called from an instance of SpecialEmployee.

public class RegularEmployee {
    private BigDecimal salary;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }

    public BigDecimal calculateBonus() {
        try { 
            return salary.multiply((BigDecimal) this.getClass().getMethod("getBonusMultiplier").invoke(null)); 
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { 
            Logger.getLogger(Parent.class.getName()).log(Level.SEVERE, null, ex); 
        }
    }

    /* ... presumably lots of other code ... */
}

public class SpecialEmployee extends RegularEmployee {
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

This does call the getBonusMultiplier() from SpecialEmployee if called from a SpecialEmployee instance.

My question is: why doesn't Java provide implicit use of Method class' invoke like this? The code would be much more readable if I could achieve the same with:

public class RegularEmployee {
    private BigDecimal salary;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }

    public BigDecimal calculateBonus() {
        return salary.multiply(origin.getBonusMultiplier());
    }

    /* ... presumably lots of other code ... */
}

public class SpecialEmployee extends RegularEmployee {
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

EDIT: So it seems like people consider my design bad. Therefore I'll try to improve on it:

public class RegularEmployee {

    private BigDecimal salary;
    protected bonusMultiplier;

    public RegularEmployee(){
        bonusMultiplier = getBonusMultiplier();
    }

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }

    public BigDecimal calculateBonus() {
        return salary.multiply(bonusMultiplier);
    }

    /* ... presumably lots of other code ... */
}

public class SpecialEmployee extends RegularEmployee {

    public SpecialEmployee(){
        bonusMultiplier = getBonusMultiplier();
    }

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

It works, it doesn't misuse Reflection and gives customizability to the getBonusMultiplier, just like the original example. There is only one thing I don't like about this piece of code: having to copy code from the constructor of RegularEmployee to the constructor of SpecialEmployee. I know that they are semantically different due to hiding the parent method, but still, I prefer not having to copy code. Can anyone give me a solution without having to copy code? (Or in other words: can anyone give me a lazy programmers solution? :P)

Community
  • 1
  • 1
Rik Schaaf
  • 1,101
  • 2
  • 11
  • 30
  • 4
    My question is: why `getBonusMultiplier` method is `static`? This is the root of the problem: a bad design. – Luiggi Mendoza Apr 17 '15 at 05:37
  • By the way, the answer is already stated in the question and accepted answer in the link you provide in the question. – Luiggi Mendoza Apr 17 '15 at 05:37
  • Pretty much what Luiggi said. Instance methods use an instance. Static methods are independent of any instance (other than the arguments). If you want a method that returns a different value depending on the instance, then make it an instance method. – ajb Apr 17 '15 at 05:41
  • 2
    'Implicit reflection' is a poor description of the mechanism you are asking for. – user207421 Apr 17 '15 at 05:44
  • Indeed, I misused the word Reflection, however Method is part of java.lang.reflect. – Rik Schaaf Apr 17 '15 at 05:50
  • Reflection doesn't need to be that readable; it should be used extremely rarely. – Louis Wasserman Apr 17 '15 at 06:08
  • @LouisWasserman I only use Reflection (or call it what you want) because the language doesn't provide the needed way to call a static method based on run-time type. – Rik Schaaf Apr 17 '15 at 06:14
  • That's generally a symptom of a design problem suggesting the method shouldn't be static. – Louis Wasserman Apr 17 '15 at 06:15
  • @ajb why would you need to have an instance if you want to know a property of a class? I'd say it is a bad design if you need to create a dummy variable to get a property of a class that doesn't depend on which object accesses it – Rik Schaaf Apr 17 '15 at 06:16
  • '`Method` is a part of `java.lang.Reflect`' doesn't have anything to do with your question either. What you are asking about is given exactly by the title of the duplicate: 'Why doesn't Java allow overriding of static methods?' – user207421 Apr 17 '15 at 06:21
  • You're trying to get a property of a class, where you don't know what class you're querying until run time. To do that with a method like you have, the method needs to be polymorphic--i.e. the program decides at run time which method to call. **And a static method is by definition not polymorphic**. Therefore you cannot solve your problem with a static method, like you were trying to do. – ajb Apr 18 '15 at 04:02
  • Also, regarding your comment about needing to create a dummy variable: it's rare that you need to. In your example, why would you need to get the bonus multiplier, unless you already have a (non-dummy) instance of an employee and you're trying to compute their bonus? If you really do have a use case where you need the bonus multiplier without an actual employee, start a new question. I think we can find a way to solve your problem without using reflection. – ajb Apr 18 '15 at 04:13
  • Also, in your last example, if you remove `static` from `getBonusMultiplier`, then `salary.multiply(getBonusMultiplier())` would work--you don't need to have a variable save the value of the bonus. `getBonusMultiplier()` would not be static, therefore it is polymorphic and will return the correct value based on whether the employee is a `RegularEmployee` or `SpecialEmployee`. Try it. – ajb Apr 18 '15 at 04:14
  • @ajb that is true, but getBonusMultiplier doesn't depend on the object. And bear in mind, it is only an example. The thing is: if you want a class dependent method (a method that doesn't use non-static fields/methods) which depends on its run-time type, how do you structure your code so that you don't have to create a dummy object to invoke the method on? In my opinion Java lacks this ability without using workarounds like I did. – Rik Schaaf Apr 27 '15 at 15:19
  • Please read my earlier comments. You should not need a dummy object. You want to compute an employee's bonus, pass the employee object. This is not a dummy object (although I've worked with some employees who were dummies). I think the trouble is, you're fixated on the idea that if a method doesn't use any employee data, it should be "static" or something else that's different from an instance method. That idea is wrong--please let it go! The bonus does depend on employee data, because the employee's type (class) **is** data. So it doesn't use any "fields"? Irrelevant. – ajb Apr 28 '15 at 06:07
  • @ajb Ok, the part of your argument that the class is part of the data and therefore the method should not be static is a solid argument. I now completely agree with that. The part that you talk about not needing a dummy confuses me, because you talk about the bonus (which is indeed employee dependent) in stead of the bonus multiplier (which is only dependent on the class). Say there aren't any employees yet (you still have to hire them) and you want to know the bonus multiplier of the class: how would you try to get it if the method wasn't static? – Rik Schaaf Apr 30 '15 at 02:00
  • I mentioned this in an earlier comment: "If you really do have a use case where you need the bonus multiplier without an actual employee, start a new question." If you always know what class you want, then you could just declare a `public final static BigDecimal BONUS_MULTIPLIER` constant in each class, and use `RegularEmployee.BONUS_MULTIPLIER`, `SpecialEmployee.BONUS_MULTIPLIER`, etc. It doesn't even matter whether the constant names are the same.... – ajb Apr 30 '15 at 05:17
  • 1
    ... But if you have a `Class>` object and want to get the bonus multiplier for that class, that's different. If you really want to do that, I'd post a new question and see how others handle it. I'm betting they won't use reflection; some might keep a global `Map,BigDecimal>` and set up a static initializer in each class to populate the map, but I'd be interested in seeing how others handle this. It's not a use case I've ever had to deal with. – ajb Apr 30 '15 at 05:19
  • @ajb Thanks for your contribution to this question. Though the question was mostly a philosophical question regarding the meaning of static in the java programming language and how it is used in non-static methods, you did help me understand my own question better. But here is a question for you: Say you had a class and a child class. Both have access to a resource of data which would preferably be accessed in a similar way (like is used in the calculateBonus from my example), but the value of the resource is class specific (note, not object specific). How would you tackle such a problem? – Rik Schaaf Dec 16 '15 at 23:13

0 Answers0