public List<BeanSpec> fields($.Predicate<Field> filter) {
        List<BeanSpec> retVal = new ArrayList<>();
        BeanSpec current = this;
        Class<?> rawType = rawType();
        if ($.isSimpleType(rawType)) {
            return C.list();
        }
        Type[] typeDeclarations = rawType.getTypeParameters();
        Map<String, Class> typeImplLookup = null;
        while (null != current && current.isNotObject()) {
            Type[] fieldTypeParams = null;
            Type[] classTypeParams;
            Map<String, Type> typeParamsMapping = new HashMap<>();
            for (Field field : current.rawType().getDeclaredFields()) {
                if (!filter.test(field)) {
                    continue;
                }
                Type fieldGenericType = field.getGenericType();
                if (fieldGenericType instanceof ParameterizedType) {
                    if (null == fieldTypeParams) {
                        fieldTypeParams = ((ParameterizedType) fieldGenericType).getActualTypeArguments();
                        if (fieldTypeParams != null && fieldTypeParams.length > 0) {
                            classTypeParams = current.rawType().getTypeParameters();
                            if (classTypeParams != null && classTypeParams.length > 0) {
                                List<Type> classTypeImpls = current.typeParams();
                                for (int i = 0; i < classTypeParams.length; ++i) {
                                    if (i >= classTypeImpls.size()) {
                                        break;
                                    }
                                    Type classTypeParam = classTypeParams[i];
                                    if (classTypeParam instanceof TypeVariable) {
                                        typeParamsMapping.put(((TypeVariable) classTypeParam).getName(), classTypeImpls.get(i));
                                    }
                                }
                            }
                        }
                    }
                    boolean updated = false;
                    if (!typeParamsMapping.isEmpty()) {
                        for (int i = 0; i < fieldTypeParams.length; ++i) {
                            Type typeArg = fieldTypeParams[i];
                            if (typeArg instanceof TypeVariable) {
                                String name = ((TypeVariable) typeArg).getName();
                                Type typeImpl = typeParamsMapping.get(name);
                                if (null != typeImpl && $.ne(typeImpl, typeArg)) {
                                    updated = true;
                                    fieldTypeParams[i] = typeImpl;
                                }
                            }
                        }
                    }
                    if (updated) {
                        fieldGenericType = new ParameterizedTypeImpl(fieldTypeParams, ((ParameterizedType) fieldGenericType).getOwnerType(), ((ParameterizedType) fieldGenericType).getRawType());
                    }
                    retVal.add(beanSpecOf(field, fieldGenericType));
                } else if (fieldGenericType instanceof TypeVariable) {
                    if (null == typeImplLookup) {
                        typeImplLookup = Generics.buildTypeParamImplLookup(this.rawType);
                        if (null != fieldTypeImplLookup) {
                            typeImplLookup.putAll(fieldTypeImplLookup);
                        }
                    }
                    TypeVariable fieldType = (TypeVariable) fieldGenericType;
                    Class clazz = typeImplLookup.get(fieldType.getName());
                    if (null != clazz) {
                        retVal.add(of(field, fieldGenericType, injector, typeImplLookup));
                    } else {
                        boolean added = false;
                        for (int i = typeDeclarations.length - 1; i >= 0; --i) {
                            if (typeDeclarations[i].equals(fieldGenericType)) {
                                fieldGenericType = current.typeParams().get(i);
                                retVal.add(of(field, fieldGenericType, injector, typeImplLookup));
                                added = true;
                                break;
                            }
                        }
                        if (!added) {
                            throw new InjectException("Cannot infer field type: " + field);
                        }
                    }
                } else if (fieldGenericType instanceof GenericArrayType) {
                    if (null == typeImplLookup) {
                        typeImplLookup = Generics.buildTypeParamImplLookup(this.rawType);
                    }
                    retVal.add(of(field, injector(), typeImplLookup));
                } else {
                    retVal.add(of(field, injector()));
                }
            }
            current = current.parent();
            if (null != current) {
                typeDeclarations = current.rawType.getTypeParameters();
            }
        }
        return retVal;
    }
public List < BeanSpec > fields ( $ . Predicate < Field > filter ) { List < BeanSpec > retVal = new ArrayList < > ( ) ; BeanSpec current = this ; Class < ? > rawType = rawType ( ) ; if ( $ . isSimpleType ( rawType ) ) { return C . list ( ) ; } Type [ ] typeDeclarations = rawType . getTypeParameters ( ) ; Map < String , Class > typeImplLookup = null ; while ( null != current && current . isNotObject ( ) ) { Type [ ] fieldTypeParams = null ; Type [ ] classTypeParams ; Map < String , Type > typeParamsMapping = new HashMap < > ( ) ; for ( Field field : current . rawType ( ) . getDeclaredFields ( ) ) { if ( ! filter . test ( field ) ) { continue ; } Type fieldGenericType = field . getGenericType ( ) ; if ( fieldGenericType instanceof ParameterizedType ) { if ( null == fieldTypeParams ) { fieldTypeParams = ( ( ParameterizedType ) fieldGenericType ) . getActualTypeArguments ( ) ; if ( fieldTypeParams != null && fieldTypeParams . length > 0 ) { classTypeParams = current . rawType ( ) . getTypeParameters ( ) ; if ( classTypeParams != null && classTypeParams . length > 0 ) { List < Type > classTypeImpls = current . typeParams ( ) ; for ( int i = 0 ; i < classTypeParams . length ; ++ i ) { if ( i >= classTypeImpls . size ( ) ) { break ; } Type classTypeParam = classTypeParams [ i ] ; if ( classTypeParam instanceof TypeVariable ) { typeParamsMapping . put ( ( ( TypeVariable ) classTypeParam ) . getName ( ) , classTypeImpls . get ( i ) ) ; } } } } } boolean updated = false ; if ( ! typeParamsMapping . isEmpty ( ) ) { for ( int i = 0 ; i < fieldTypeParams . length ; ++ i ) { Type typeArg = fieldTypeParams [ i ] ; if ( typeArg instanceof TypeVariable ) { String name = ( ( TypeVariable ) typeArg ) . getName ( ) ; Type typeImpl = typeParamsMapping . get ( name ) ; if ( null != typeImpl && $ . ne ( typeImpl , typeArg ) ) { updated = true ; fieldTypeParams [ i ] = typeImpl ; } } } } if ( updated ) { fieldGenericType = new ParameterizedTypeImpl ( fieldTypeParams , ( ( ParameterizedType ) fieldGenericType ) . getOwnerType ( ) , ( ( ParameterizedType ) fieldGenericType ) . getRawType ( ) ) ; } retVal . add ( beanSpecOf ( field , fieldGenericType ) ) ; } else if ( fieldGenericType instanceof TypeVariable ) { if ( null == typeImplLookup ) { typeImplLookup = Generics . buildTypeParamImplLookup ( this . rawType ) ; if ( null != fieldTypeImplLookup ) { typeImplLookup . putAll ( fieldTypeImplLookup ) ; } } TypeVariable fieldType = ( TypeVariable ) fieldGenericType ; Class clazz = typeImplLookup . get ( fieldType . getName ( ) ) ; if ( null != clazz ) { retVal . add ( of ( field , fieldGenericType , injector , typeImplLookup ) ) ; } else { boolean added = false ; for ( int i = typeDeclarations . length - 1 ; i >= 0 ; -- i ) { if ( typeDeclarations [ i ] . equals ( fieldGenericType ) ) { fieldGenericType = current . typeParams ( ) . get ( i ) ; retVal . add ( of ( field , fieldGenericType , injector , typeImplLookup ) ) ; added = true ; break ; } } if ( ! added ) { throw new InjectException ( "Cannot infer field type: " + field ) ; } } } else if ( fieldGenericType instanceof GenericArrayType ) { if ( null == typeImplLookup ) { typeImplLookup = Generics . buildTypeParamImplLookup ( this . rawType ) ; } retVal . add ( of ( field , injector ( ) , typeImplLookup ) ) ; } else { retVal . add ( of ( field , injector ( ) ) ) ; } } current = current . parent ( ) ; if ( null != current ) { typeDeclarations = current . rawType . getTypeParameters ( ) ; } } return retVal ; }
Returns a list of `BeanSpec` for all fields including super class fields.

@param filter
a function to filter out fields that are not needed.
@return the fields spec list