Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Java Java Annotations Writing Your Own Annotation Putting It All Together

Final challenge - stuck on last two tasks..

I have obviously missed something I was supposed to have grasped...

Test.java
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD}) 
public @interface Test { 
   int num() default 1;
     boolean enabled() default true;
   String stringy() default "stupid";
}
SocialNetworkTest.java
public class SocialNetworkTest {
  @Test(num = 2)
  public void testTweet() {

  }
  @Test(enabled = false)
  public void testInsta() { 

  }
  @Test(stringy = "something")
  public void testFacebook() { 

  }
  public void testPinterest() { 

  }

  public void testSnapchat() { 

  }
}
TestAnalyzer.java
public class TestAnalyzer {
  /**
   *  Counts the number of methods in the class given by `clazz` that have been annotated
   *  with the @Test annotation.
   */
  public static int getNumAnnotatedMethods(Class<?> clazz) {
    return 0;  
  }
}

The code to 'Test.java' gets past the first two challenges.

The @Test lines of code added to 'SocialNetworkTest.java' just gives me:

Bummer! It appears that at least one of your annotations either omits one of your defined elements in the @Test annotation or has used the same value as the default.

The Preview screen showing output.txt shows no compile errors so the java code is good --just not what the answer analyzer is looking for..

Hmmmm..?


Well, since neither the "using-reflection-on-our-annotation" video or

the final zip download file seems to have an example of

defining custom annotation elements and then using them without default values

I found/followed theses examples:

http://www.mkyong.com/java/java-custom-annotations-example/

http://softwarecave.org/2014/05/02/custom-annotations-in-java/


So, okay, here's another permutation (based on that last example).

What if it wants both elements defined in Test.java to show non-default elements?

So I changed the two sets of code

Test.java
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD}) 
public @interface Test { 
   int num() default 1;
   boolean enabled() default true;
}
SocialNetworkTest.java
public class SocialNetworkTest {
  @Test(num = 2, enabled = false)
  public void testTweet() {

  }
  @Test(num = 4, enabled = false)
  public void testInsta() { 

  }
  @Test(num = 5, enabled = false)
  public void testFacebook() { 

  }
  public void testPinterest() { 

  }

  public void testSnapchat() { 

  }
}

...and lo and behold, the Bummer is gone.

Don't really understand why, but it works?!


On to task 4 of 4.

TestAnalyzer.java
import java.lang.reflect.Method;

public class TestAnalyzer {
  /**
   *  Counts the number of methods in the class given by `clazz` that have been annotated
   *  with the @Test annotation.
   */
  public static int getNumAnnotatedMethods(Class<?> clazz) {
    int count = 0;
    for (Method m : clazz.getMethods()) { 
        //if (m.hasAnnotation()) {
          count++;
          //}  
        }
        return count;
  }
}

This gives:

Bummer! Your method is not returning the correct number. Do you have a loop that iterates over the declared methods of clazz, keeping a count of the ones where hasAnnotation is true?

However (and this is a big thing): no compiler errors in the Preview output.txt (Yeah!)


If I get rid of the commenting it gives:

Bummer! Oops, there is a compiler error! Don't forget to add import statements for any outside classes you're using.

output.txt
./TestAnalyzer.java:11: error: cannot find symbol
          if (m.hasAnnotation()) {
               ^
  symbol:   method hasAnnotation()
  location: variable m of type Method
1 error

Also tried changing things to:

import java.lang.reflect.Method;

public class TestAnalyzer {
  /**
   *  Counts the number of methods in the class given by `clazz` that have been annotated
   *  with the @Test annotation.
   */
  public static int getNumAnnotatedMethods(Class<?> clazz, Class<?> annotation) {
    int count = 0;
    for (Method m : clazz.getMethods()) { 
          if (m.hasAnnotation(annotation)) {
          count++;
          }  
        }
        return count;
  }
}

..but it just gives me another compiler error:

output.txt
JavaTester.java:203: error: method getNumAnnotatedMethods in class TestAnalyzer cannot be applied to given types;
int actual = TestAnalyzer.getNumAnnotatedMethods(sampleClass);
                         ^
  required: Class,Class
  found: Class
  reason: actual and formal argument lists differ in length
./TestAnalyzer.java:11: error: cannot find symbol
          if (m.hasAnnotation(annotation)) {
               ^
  symbol:   method hasAnnotation(Class)
  location: variable m of type Method
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
2 errors

So is it really another import statement that is missing or the wrong way code for testing hasAnnotation?

Who knows..time to give up for now and go to sleep..zzzzzzz

7 Answers

From the last output.html above I felt I was getting close.

Played with it for another hour and finally came up with:

import java.lang.reflect.Method;

public class TestAnalyzer {
  /**
   *  Counts the number of methods in the class given by `clazz` that have been annotated
   *  with the @Test annotation.
   */
  public static int getNumAnnotatedMethods(Class<?> clazz) {
    int count = 0;
    for (Method m : clazz.getMethods()) { 
          if (m.isAnnotationPresent(Test.class)) {
          count++;
          }  
        }
        return count;
  }
}

Did I learn anything - absolutely not.

It was pure brute force trial and error that came up with the code that passed!

Hi Chris, Thanks for correcting the misleading error message.

I'm sure I'm not the only only that got confused by the referencing of a hasAnnotation method.


As regard the specific "method signature" needed, I'm not sure what you mean..

I guess I will have to do some more research latter to come to an understanding of just exactly why I wouldn't be able get it to pass just by adding a Class parameter to getNumAnnotatedMethods.


I also noticed that at this point in time there aren't many forum threads that have been started for this course yet.

Maybe people are still working through it or haven't even had time to start the course yet (with all other content TeamTreehouse provides).

I guess I'll wait a while to see if anyone else was confused by (or stuck on) the last task of the last challenge.


EDIT (later):

Have to go to work, but spent a few minutes researching 'isAnnotationPresent'

.

Of course because the course has no .srt video transcipt file it's pretty much impossible

to search the videos for this term using Google search,

so here's some semi-random non-Treehouse-video related results:

http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/AnnotatedElement.html

http://www.tutorialspoint.com/java/lang/package_isannotationpresent.htm

http://www.java2s.com/Tutorials/Java/java.lang/Class/Java_Class_isAnnotationPresent_Class_lt_extends_Annotation_gt_annotationClass_.htm

http://stackoverflow.com/questions/7599006/isannotationpresent-return-false-when-used-with-super-type-reference-in-java

http://stackoverflow.com/questions/13858651/check-if-derived-class-have-an-annotation-from-super-class

http://stackoverflow.com/questions/9835973/why-isannotationpresentid-class-is-not-working-for-the-class-file

http://www.coderanch.com/t/513044/java/java/isAnnotationPresent-returning-false

https://keyholesoftware.com/2014/09/15/java-annotations-using-reflection/

https://www.altamiracorp.com/blog/employee-posts/inheriting-annotations-on-java

http://isagoksu.com/2009/creating-custom-annotations-and-making-use-of-them/

http://codereview.stackexchange.com/questions/42332/model-simulation-using-java-annotations

https://books.google.com/books?id=CwSaQpCtfPkC&pg=PA193&lpg=PA193&dq=java+if+isAnnotationPresent&source=bl&ots=dHK3fXEwVJ&sig=ycedR25xY9IRvCmS9Uayi02qPE0&hl=en&sa=X&ved=0CB0Q6AEwADgUahUKEwjro472rKnIAhXJLIgKHb8yB1Q#v=onepage&q=java%20if%20isAnnotationPresent&f=false

Of course none of these give an example of using 'isAnnotationPresent' in a loop

with count ++ as well as an If test.

Found this github code which had several examples of using 'isAnnotationPresent' in different ways (which was very valuable):

https://github.com/karuradev/karura/blob/master/autogen/com/karura/autogen/codegen/PluginGenerator.java

Digging thru that karuradev github, this particular code snippet was the most interesting (for its simplicity -- although the 'SupportJavascriptInterface.class' is probably doing most of the "heavy lifting" here):

@SuppressLint("NewApi")
private int signatureCount(Class<?> clazz) {
int count = 0;
for (Method method : clazz.getDeclaredMethods()) {
if (!method.isAnnotationPresent(SupportJavascriptInterface.class)) {
continue;
}
count++;
}
return count;
} 

Lastly (from a satisficing heuristic result possibly the most relevant example), I found this stackoverflow thread:

http://stackoverflow.com/questions/1833533/java-reflection-count-of-methods-with-an-annotation

..which had this code:

public class AnnotationUtils {
    public static int countMethodsWithAnnotation(Class<?> klass,
                                                 Class<?> annotation) {
        int count = 0;
        for (Method m : klass.getMethods()) {
            if (m.isAnnotationPresent(annotation)) {
                count++;
            }
        }
        return count;
    }
    // Other methods for custom annotation processing
}

Similar type code found at the bottom of this stackoverflow thread:

http://stackoverflow.com/questions/2685404/how-to-get-number-of-attributes-in-a-java-class

.

Will have to fool around though (using all of the above) to develop a challenge solution, later...


incredibly INCREDIBLY tedious --but I went through the last reflection video second by second:

03:03 If method.isAnnotationPresent(Doc.class)) {

03:48 Doc doc = method.getAnnotation(Doc.class);

04:38 int annotated ParamCount = doc.params().length;

Of course this helps me not-at-all in answering task 4 of 4 because there is no reference to 'doc'.

Truly a bummer (and very frustrating), it's almost like this final challenge

had absolutely nothing to do with the code that was presented in this course.

Tobias Mahnert
Tobias Mahnert
89,414 Points

JM this line of code if (m.isAnnotationPresent())

needs to be

if (m.isAnnotationPresent(Test.class))

Chris Ramacciotti
seal-mask
STAFF
.a{fill-rule:evenodd;}techdegree
Chris Ramacciotti
Treehouse Guest Teacher

Hi James,

Thank you for your feedback. As for the first of the two tasks you were having difficulty with, yes, it was intended that you specify non-default values for each of the two elements you included in your annotation definition. Looks like you arrived at the solution, so nicely done there.

As for the one you're stuck on - I apologize. There was a misleading error message that I've corrected. The error message references a hasAnnotation method. This is incorrect. The method name, as used in the videos, is isAnnotationPresent. When you use that method, don't forget to pass the Class object of the Test annotation. Also, the tests won't pass by adding a Class parameter to getNumAnnotatedMethods since my tests specifically need the provided method signature. Your decision to do this would be a great one in a scenario where you also write the call to your method. Alas, I must call it here. :)

No worries though, you don't need a parameter since you know the value of the Class that you must pass to the isAnnotationPresent method. When you wake up from those zzzzzzz's (and I do hope they were good zzzzzzz's), give it a shot.

Hello Chris, Why should i pass Test.class and not SocialNetworkTest.class ??? since all the "@Test" is inside SocialNetworkTest.class!!!

Back at it again...

Here's the current task 4 of 4 code I'm working with:

import java.lang.reflect.Method;

public class TestAnalyzer {
  /**
   *  Counts the number of methods in the class given by `clazz` that have been annotated
   *  with the @Test annotation.
   */
  public static int getNumAnnotatedMethods(Class clazz) {
    int count = 0;
    for (Method m : clazz.getMethods()) { 
          if (m.isAnnotationPresent()) {
          count++;
          }  
        }
        return count;
  }
}

When I Preview on this code I get:

Bummer! Oops, there is a compiler error! Don't forget to add import statements for any outside classes you're using. Click Preview to see the exact message

Of course the import statement is there.

The compiler (output.html) error is:

./TestAnalyzer.java:11: error: no suitable method found for isAnnotationPresent(no arguments)
          if (m.isAnnotationPresent()) {
               ^
    method AnnotatedElement.isAnnotationPresent(Class) is not applicable
      (actual and formal argument lists differ in length)
    method AccessibleObject.isAnnotationPresent(Class) is not applicable
      (actual and formal argument lists differ in length)
1 error

...also tried:

import java.lang.reflect.Method;

public class TestAnalyzer {
  /**
   *  Counts the number of methods in the class given by `clazz` that have been annotated
   *  with the @Test annotation.
   */
  public static int getNumAnnotatedMethods(Class<?> clazz,
                                           Class<?> annotation) {
    int count = 0;
    for (Method m : clazz.getMethods()) { 
          if (m.isAnnotationPresent(annotation)) {
          count++;
          }  
        }
        return count;
  }
}

..which gave the following for output.html:

JavaTester.java:203: error: method getNumAnnotatedMethods in class TestAnalyzer cannot be applied to given types;
int actual = TestAnalyzer.getNumAnnotatedMethods(sampleClass);
                         ^
  required: Class,Class
  found: Class
  reason: actual and formal argument lists differ in length
./TestAnalyzer.java:12: error: no suitable method found for isAnnotationPresent(Class)
          if (m.isAnnotationPresent(annotation)) {
               ^
    method AnnotatedElement.isAnnotationPresent(Class) is not applicable
      (argument mismatch; Class cannot be converted to Class)
    method AccessibleObject.isAnnotationPresent(Class) is not applicable
      (argument mismatch; Class cannot be converted to Class)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
2 errors

Also tried this code:

import java.lang.reflect.Method;

public class TestAnalyzer {
  /**
   *  Counts the number of methods in the class given by `clazz` that have been annotated
   *  with the @Test annotation.
   */
  public static int getNumAnnotatedMethods(Class<?> clazz) {
    int count = 0;
    for (Method m : clazz.getMethods()) { 
          if (m.isAnnotationPresent(Class)) {
          count++;
          }  
        }
        return count;
  }
}

..which gave the same Bummer! mesage, but a different output.html:

./TestAnalyzer.java:11: error: cannot find symbol
          if (m.isAnnotationPresent(Class)) {
                                    ^
  symbol:   variable Class
  location: class TestAnalyzer
1 error
Chris Ramacciotti
seal-mask
STAFF
.a{fill-rule:evenodd;}techdegree
Chris Ramacciotti
Treehouse Guest Teacher

Glad you're back at it! It looks like your call to m.isAnnotationPresent() is still missing the required Class parameter, which is what I referenced a couple times in my last response. Also, the code snippet you scraped from 3:03 is exactly like the one you'll need here, except that you don't want to pass Doc.class, but rather _____.class.

In case you're looking for the docs on the isAnnotationPresent method, here it is:

https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html#isAnnotationPresent-java.lang.Class-

Though you call this method on a Method object (kind of meta, I know - but such is the nature of reflection) the method itself is actually inherited from the AccessibleObject class, which is in the inheritance chain when climbing from the Method class all the way up to the Object class. In any case, notice that the isAnnotationPresent method requires a Class object as its only parameter.

Try passing a reference to your annotation's class (like the one mentioned at 3:03), and see if it passes. I smell success in your near future.

Hello Chris, why passing Test.Class works but not SocialNetworkTest.class??? All the "@Test" is in the SocialNetworkTest.class

Chris Needham
Chris Needham
1,871 Points

Yeah, the videos didn't really seem to point too much to the last two assignments.... And there's not much coding... it's supposed to be simple but it's too abstractly coded. I hate bizarre code like that.