23

Am trying to obtain and invoke a protected method residing in a different class and also different package using Java Reflection.

Class containing protected method:

package com.myapp;

public class MyServiceImpl {

   protected List<String> retrieveItems(String status) {
         // Implementation
   }
}

Calling class:

package xxx.myapp.tests;

import com.myapp.MyServiceImpl;

public class MyTestCase {

    List<String> items;

    public void setUp() throws Exception {

         MyServiceImpl service = new MyServiceImpl();
         Class clazz = service.getClass();

         // Fails at the next line:
         Method retrieveItems = clazz.getDeclaredMethod("retrieveItems");

         // How to invoke the method and return List<String> items?
         // tried this but it fails?
         retrieveItems.invoke(clazz, "S");
    }
}

The compiler throws this Exception:

java.lang.NoSuchMethodException: com.myapp.MyServiceImpl.retrieveItems()
Erik
  • 503
  • 1
  • 7
  • 26
PacificNW_Lover
  • 4,746
  • 31
  • 90
  • 144

5 Answers5

28

The problem with your code is that the getDeclaredMethod function looks up a function both by name and by argument types. With the call

Method retrieveItems = clazz.getDeclaredMethod("retrieveItems");

The code will look for a method retrieveItems() with no arguments. The method you're looking for does take an argument, a string, so you should call

Method retrieveItems = clazz.getDeclaredMethod("retrieveItems", String.class);

This will tell Java to search for retrieveItems(String), which is what you're looking for.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 1
    +1 - this is the problem that the exception is flagging. Once the method has been retrieved, it must be made accessible as per @jk's answer. – Stephen C Jan 12 '11 at 00:27
  • The answer by @jk was deleted. The relevant code is `retrieveItems.setAccessible(true);` This code still works and must be run before invoking a protected method. – Jason Nov 17 '22 at 17:38
7

You should to use link to created object instead of link to class in invoke method and use Method.setAccessible(true) invocation for unlock access:

public void setUp() throws Exception {
    MyServiceImpl service = new MyServiceImpl();
    Class<?> clazz = service.getClass();
    Method retrieveItems = clazz.getDeclaredMethod("retrieveItems", String.class);
    retrieveItems.setAccessible(true);
    items = (List<String>)retrieveItems.invoke(service, "S");
}
BeCase
  • 141
  • 2
  • 5
6

Instead of using that tricky reflection stuff, why not simply create a derived class, which will have access to the protected method?

See Is it bad practice to use Reflection in Unit testing? for further thoughts.

Community
  • 1
  • 1
Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • 13
    -1 -- it does not answer the root question. How do you access a protected method via reflection in Java? – BrainSlugs83 Sep 27 '14 at 23:20
  • 1
    @BrainSlugs83 Reread the title of the question. The OP actually wants to test some code, but incorrectly thinks they need to use reflection. They have an XY Problem. My answer speaks to their real problem, not their proposed solution. – Raedwald Sep 28 '14 at 00:11
  • 4
    -1 -- "Am trying to obtain and invoke a protected method residing in a different class and also different package using Java Reflection." That's the intent; how do you know this isn't something like a school assignment? It seems like telling him to create a derived class (which leads to a lot more lines of code) is like answering the question "I want to open a can of tuna with my knife" with "use a can opener." – Sandy Simonton Mar 11 '16 at 00:52
4

If you put your testcases in the same package (com.myapp instead of com.myapp.tests) they will have access to protected (and default package level) members.

Then you can call service.retrieveMembers(status) directly.

If you are trying to separate the source from the tests, its usually better to use a different source directory (for instance a src directory and a test directory).

pringi
  • 3,987
  • 5
  • 35
  • 45
Nick
  • 393
  • 2
  • 5
  • If the tests are in the same package then you your compiled class files are most likely going to be in the same target directory, which would bloat any deployment file with a bunch of test class files unless you go through every package/project you have and define exclusion rules for the target directory. Not a good idea in my opinion. – Sandy Simonton Mar 11 '16 at 00:46
  • That's why you use a different source directory for your tests. That's the standard way tools like maven work. – Nick Mar 07 '17 at 19:52
  • Yeah, I misunderstood your post. I see what you're saying now, thanks! – Sandy Simonton Mar 13 '17 at 16:58
2

No reflection or inheritance is needed:

Put your MyTestCase under package com.myapp, as scope 'protected' is also 'package'. Then MyTestCase can access protected methods of MyServiceImpl.

卢声远 Shengyuan Lu
  • 31,208
  • 22
  • 85
  • 130
  • If the tests are in the same package then you your compiled class files are most likely going to be in the same target directory, which would bloat any deployment file with a bunch of test class files unless you go through every package/project you have and define exclusion rules for the target directory. Not a good idea in my opinion. – Sandy Simonton Mar 11 '16 at 00:46
  • 1
    @SandySimonton, It can be achieved. We can make our project structure as follows (maven): `src->main->java` contains all the code. `src->test->java` contains all the tests. –  May 17 '17 at 05:32