@@ -66,8 +66,8 @@ static <T> PojoType<T> of(Class<T> pojoClass) {
6666 Constructor <T > ctor = requirePublicDefaultConstructor (pojoClass );
6767
6868 Map <String , Field > allFields = discoverAllInstanceFields (pojoClass );
69- Map <String , Method > getters = discoverGetters (pojoClass );
70- Map <String , Method > setters = discoverSetters (pojoClass );
69+ Map <String , Method > getters = discoverGetters (pojoClass , allFields );
70+ Map <String , Method > setters = discoverSetters (pojoClass , allFields );
7171
7272 Map <String , Property > props = new LinkedHashMap <>();
7373 for (Map .Entry <String , Field > e : allFields .entrySet ()) {
@@ -85,10 +85,18 @@ static <T> PojoType<T> of(Class<T> pojoClass) {
8585 if (!publicField ) {
8686 // When not a public field, require both getter and setter
8787 if (getter == null || setter == null ) {
88+ if (field .getName ().startsWith ("this$" )) {
89+ final Class type = field .getType ();
90+ if ((type .getName () + "$" + pojoClass .getSimpleName ())
91+ .equals (pojoClass .getName ())) {
92+ continue ;
93+ }
94+ }
95+ final String capitalizedName = capitalize (name );
8896 throw new IllegalArgumentException (
8997 String .format (
9098 "POJO class %s field '%s' must be public or have both getter and setter (get%s/set%s)." ,
91- pojoClass .getName (), name , capitalize ( name ), capitalize ( name ) ));
99+ pojoClass .getName (), name , capitalizedName , capitalizedName ));
92100 }
93101 }
94102 props .put (
@@ -108,22 +116,30 @@ private static <T> void validatePublicClass(Class<T> pojoClass) {
108116 }
109117
110118 private static <T > Constructor <T > requirePublicDefaultConstructor (Class <T > pojoClass ) {
111- try {
112- Constructor <T > ctor = pojoClass .getDeclaredConstructor ();
113- if (!Modifier .isPublic (ctor .getModifiers ())) {
114- throw new IllegalArgumentException (
115- String .format (
116- "POJO class %s must have a public default constructor." ,
117- pojoClass .getName ()));
119+ Constructor <T >[] ctors = (Constructor <T >[]) pojoClass .getConstructors ();
120+ for (Constructor <T > c : ctors ) {
121+ if (!Modifier .isPublic (c .getModifiers ())) {
122+ continue ;
123+ }
124+ if (c .getParameterCount () == 0 ) {
125+
126+ return c ;
127+ }
128+ if (c .getParameterCount () == 1
129+ && pojoClass
130+ .getName ()
131+ .equals (
132+ c .getParameterTypes ()[0 ].getName ()
133+ + "$"
134+ + pojoClass .getSimpleName ())) {
135+ return c ;
118136 }
119- return ctor ;
120- } catch (NoSuchMethodException e ) {
121- throw new IllegalArgumentException (
122- String .format (
123- "POJO class %s must have a public default constructor." ,
124- pojoClass .getName ()),
125- e );
126137 }
138+
139+ throw new IllegalArgumentException (
140+ String .format (
141+ "POJO class %s must have a public default constructor." ,
142+ pojoClass .getName ()));
127143 }
128144
129145 private static Map <String , Field > discoverAllInstanceFields (Class <?> clazz ) {
@@ -143,32 +159,68 @@ private static Map<String, Field> discoverAllInstanceFields(Class<?> clazz) {
143159 return fields ;
144160 }
145161
146- private static Map <String , Method > discoverGetters (Class <?> clazz ) {
147- Map <String , Method > getters = new HashMap <>();
162+ private static Map <String , Method > discoverGetters (
163+ Class <?> clazz , Map <String , Field > fieldMap ) {
164+ final Map <String , Method > getters = new HashMap <>();
148165 for (Method m : clazz .getMethods ()) { // public methods incl. inherited
149- if (m .getParameterCount () == 0
150- && m .getName ().startsWith ("get" )
151- && !m .getReturnType ().equals (void .class )) {
152- String prop = decapitalize (m .getName ().substring (3 ));
166+ final String prop = getGetterProp (m );
167+ if (fieldMap .containsKey (prop )) {
153168 getters .put (prop , m );
154169 }
155170 }
156171 return getters ;
157172 }
158173
159- private static Map <String , Method > discoverSetters (Class <?> clazz ) {
160- Map <String , Method > setters = new HashMap <>();
174+ private static String getGetterProp (Method m ) {
175+ if (m .getParameterCount () != 0 ) {
176+ return null ;
177+ }
178+ final Class <?> returnType = m .getReturnType ();
179+ if (void .class .equals (returnType ) || Void .class .equals (returnType )) {
180+ return null ;
181+ }
182+ final String name = m .getName ();
183+ if (name .startsWith ("get" )) {
184+ return decapitalize (name .substring (3 ));
185+ }
186+ if (returnType .equals (boolean .class ) || returnType .equals (Boolean .class )) {
187+ if (name .startsWith ("is" )) {
188+ return decapitalize (name .substring (2 ));
189+ }
190+ if (name .startsWith ("has" )) {
191+ return decapitalize (name .substring (3 ));
192+ }
193+ }
194+ return null ;
195+ }
196+
197+ private static Map <String , Method > discoverSetters (
198+ Class <?> clazz , Map <String , Field > fieldMap ) {
199+ final Map <String , Method > setters = new HashMap <>();
161200 for (Method m : clazz .getMethods ()) { // public methods incl. inherited
162- if (m .getParameterCount () == 1
163- && m .getName ().startsWith ("set" )
164- && m .getReturnType ().equals (void .class )) {
165- String prop = decapitalize (m .getName ().substring (3 ));
201+ final String prop = getSetterProp (m );
202+ if (fieldMap .containsKey (prop )) {
166203 setters .put (prop , m );
167204 }
168205 }
169206 return setters ;
170207 }
171208
209+ private static String getSetterProp (Method m ) {
210+ if (m .getParameterCount () != 1 ) {
211+ return null ;
212+ }
213+ final Class <?> returnType = m .getReturnType ();
214+ if (!void .class .equals (returnType ) && !Void .class .equals (returnType )) {
215+ return null ;
216+ }
217+ final String name = m .getName ();
218+ if (name .startsWith ("set" )) {
219+ return decapitalize (name .substring (3 ));
220+ }
221+ return null ;
222+ }
223+
172224 private static String capitalize (String s ) {
173225 if (s == null || s .isEmpty ()) {
174226 return s ;
0 commit comments