private static <T> Maybe<T> tryValidateBean(
      T object, RegisteredType type, final RegisteredTypeLoadingContext context) {
    if (object == null) return Maybe.absentNull("object is null");

    if (type != null) {
      if (type.getKind() != RegisteredTypeKind.BEAN)
        return Maybe.absent("Validating a bean when type is " + type.getKind() + " " + type);
      if (!isSubtypeOf(object.getClass(), type))
        return Maybe.absent(object + " does not have all the java supertypes of " + type);
    }

    if (context != null) {
      if (context.getExpectedKind() != null && context.getExpectedKind() != RegisteredTypeKind.BEAN)
        return Maybe.absent(
            "Validating a bean when constraint expected " + context.getExpectedKind());
      if (context.getExpectedJavaSuperType() != null
          && !context.getExpectedJavaSuperType().isInstance(object))
        return Maybe.absent(
            object
                + " is not of the expected java supertype "
                + context.getExpectedJavaSuperType());
    }

    return Maybe.of(object);
  }
  /**
   * Validates that the given type matches the context (if supplied); if not satisfied. returns an
   * {@link Absent} if failed with details of the error, with {@link Absent#isNull()} true if the
   * object is null.
   */
  public static Maybe<RegisteredType> tryValidate(
      RegisteredType item, final RegisteredTypeLoadingContext constraint) {
    // kept as a Maybe in case someone wants a wrapper around item validity;
    // unclear what the contract should be, as this can return Maybe.Present(null)
    // which is suprising, but it is more natural to callers otherwise they'll likely do a separate
    // null check on the item
    // (often handling null different to errors) so the Maybe.get() is redundant as they have an
    // object for the input anyway.

    if (item == null || constraint == null) return Maybe.ofDisallowingNull(item);
    if (constraint.getExpectedKind() != null
        && !constraint.getExpectedKind().equals(item.getKind()))
      return Maybe.absent(item + " is not the expected kind " + constraint.getExpectedKind());
    if (constraint.getExpectedJavaSuperType() != null) {
      if (!isSubtypeOf(item, constraint.getExpectedJavaSuperType())) {
        return Maybe.absent(
            item + " is not for the expected type " + constraint.getExpectedJavaSuperType());
      }
    }
    return Maybe.of(item);
  }
  /**
   * validates that the given object (required) satisfies the constraints implied by the given type
   * and context object, using {@link Maybe} as the result set absent containing the error(s) if not
   * satisfied. returns an {@link Absent} if failed with details of the error, with {@link
   * Absent#isNull()} true if the object is null.
   */
  public static <T> Maybe<T> tryValidate(
      final T object,
      @Nullable final RegisteredType type,
      @Nullable final RegisteredTypeLoadingContext context) {
    if (object == null) return Maybe.absentNull("object is null");

    RegisteredTypeKind kind =
        type != null ? type.getKind() : context != null ? context.getExpectedKind() : null;
    if (kind == null) {
      if (object instanceof AbstractBrooklynObjectSpec) kind = RegisteredTypeKind.SPEC;
      else kind = RegisteredTypeKind.BEAN;
    }
    return new RegisteredTypeKindVisitor<Maybe<T>>() {
      @Override
      protected Maybe<T> visitSpec() {
        return tryValidateSpec(object, type, context);
      }

      @Override
      protected Maybe<T> visitBean() {
        return tryValidateBean(object, type, context);
      }
    }.visit(kind);
  }