在 FreeMarker 模板引擎中,类型判断是非常关键的功能之一,尤其在处理动态数据、条件渲染、模板逻辑控制时。FreeMarker 提供了一系列内置的类型判断操作符(is_*),用于判断变量的类型,从而决定如何处理或渲染数据。
一、核心概念
| 操作符 | 说明 | 对应 Java 类型 |
|---|---|---|
is_string |
判断是否为字符串类型 | String |
is_number |
判断是否为数字类型 | Number 子类(如 Integer、Double) |
is_boolean |
判断是否为布尔类型 | Boolean |
is_date |
判断是否为日期类型 | Date, Calendar 等时间类型 |
is_hash |
判断是否为哈希结构(类似 Map) | Map, JavaBean 对象 |
is_sequence |
判断是否为有序集合(类似 List) | List, Array |
⚠️ 注意: FreeMarker 的变量类型在模板中是动态的,但底层仍依赖 Java 类型。类型判断主要用于控制模板逻辑,避免错误渲染。
二、操作步骤(非常详细)
步骤 1:准备数据模型
确保你传递给模板的数据中包含各种类型的数据,如字符串、数字、布尔值、日期、集合等。
Map<String, Object> model = new HashMap<>();
model.put("name", "Alice"); // String
model.put("age", 25); // Number
model.put("isStudent", true); // Boolean
model.put("birth", new Date()); // Date
model.put("user", Map.of("id", 1, "name", "Bob")); // Map / Hash
model.put("scores", Arrays.asList(90, 85, 95)); // List / Sequence
步骤 2:在模板中使用类型判断
2.1 判断是否为字符串 is_string
<#if name is "string">
这是一个字符串:${name}
</#if>
输出:
这是一个字符串:Alice
2.2 判断是否为数字 is_number
<#if age is "number">
年龄是数字:${age}
</#if>
输出:
年龄是数字:25
2.3 判断是否为布尔值 is_boolean
<#if isStudent is "boolean">
是否是学生:${isStudent?string("是", "否")}
</#if>
输出:
是否是学生:是
2.4 判断是否为日期 is_date
<#if birth is "date">
出生日期:${birth?date}
</#if>
输出:
出生日期:2025-07-28
2.5 判断是否为哈希结构 is_hash
<#if user is "hash">
用户信息:
ID:${user.id}, 名称:${user.name}
</#if>
输出:
用户信息:
ID:1, 名称:Bob
2.6 判断是否为有序集合 is_sequence
<#if scores is "sequence">
成绩列表:
<#list scores as score>
${score}
</#list>
</#if>
输出:
成绩列表:
90
85
95
三、格式与语法说明
3.1 语法格式
<#if variable is "type">
其中 type 可以是:
stringnumberbooleandatehashsequence
3.2 ?string("true", "false") 布尔值转换技巧
${isStudent?string("是", "否")}
用于将布尔值转为中文或自定义字符串。
四、常见错误与注意事项
4.1 错误使用类型判断
错误示例:
<#if 123 is "string"> <!-- 错误 -->
错误原因: 数字 123 是 number 类型,不是 string。
解决办法: 使用 ?string 转换后再判断:
<#assign str = 123?string>
<#if str is "string">
4.2 对 null 或未定义变量使用类型判断
错误示例:
<#if unknownVar is "string"> <!-- 变量未定义 -->
错误原因: 变量不存在,会抛出异常。
解决办法: 先判断是否存在:
<#if unknownVar??>
<#if unknownVar is "string">
...
</#if>
</#if>
4.3 is_hash 和 is_sequence 判断 Map/Array
is_hash:适用于Map、JavaBeanis_sequence:适用于List、数组
注意:
Map不能使用?size,应使用size属性List可以使用?size、?first、?last等集合操作
五、使用技巧
5.1 使用 ?? 判断变量是否存在
<#if myVar??>
变量存在
</#if>
5.2 多类型判断(模拟)
FreeMarker 不支持 is_string or is_number,可以使用多个 <#if> 嵌套:
<#if value is "string">
字符串
<#elseif value is "number">
数字
</#if>
5.3 使用 <#assign> 缓存类型判断结果
<#assign isString = (value is "string")>
<#if isString>
是字符串
</#if>
避免重复判断,提高性能。
六、最佳实践
| 实践 | 说明 |
|---|---|
✅ 使用 ?? 判断变量是否存在 |
避免空指针异常 |
| ✅ 封装类型判断逻辑到宏中 | 提高复用性 |
| ✅ 在 Java 层处理复杂类型逻辑 | 提升模板可读性 |
✅ 使用 ?string("yes", "no") 显示布尔值 |
提高可读性 |
✅ 避免在 <#list> 中重复类型判断 |
优化性能 |
七、性能优化建议
| 优化点 | 说明 |
|---|---|
❌ 避免在 <#list> 中频繁类型判断 |
影响渲染效率 |
| ✅ 提前在 Java 层处理类型转换 | 如 Date -> String |
✅ 使用 <#assign> 缓存判断结果 |
避免重复计算 |
| ✅ 对大集合进行预处理 | 避免在模板中做复杂逻辑 |
八、总结
| 操作符 | 用途 | 适用对象 |
|---|---|---|
is_string |
判断是否为字符串 | String |
is_number |
判断是否为数字 | Number 子类 |
is_boolean |
判断是否为布尔值 | Boolean |
is_date |
判断是否为日期 | Date, Calendar |
is_hash |
判断是否为 Map / JavaBean | Map, Object |
is_sequence |
判断是否为 List / Array | List, Array |
九、附录:常用表达式示例汇总
| 表达式 | 示例说明 |
|---|---|
${value is "string"} |
判断是否为字符串 |
${value is "number"} |
判断是否为数字 |
${value is "boolean"} |
判断是否为布尔值 |
${value is "date"} |
判断是否为日期 |
${value is "hash"} |
判断是否为 Map |
${value is "sequence"} |
判断是否为 List |
${bool?string("是", "否")} |
布尔值转中文 |
<#if var??> |
判断变量是否存在 |
十、扩展建议
如需更复杂的类型处理(如类型转换、类型映射、多态逻辑),推荐:
- 在 Java 层处理并封装逻辑,传入模板时使用统一格式
- 使用宏(macro)封装类型判断逻辑,提高复用性和可维护性
例如:
<#macro displayValue value>
<#if value is "string">
字符串:${value}
<#elseif value is "number">
数字:${value}
<#else>
其他类型
</#if>
</#macro>
✅ 结语: FreeMarker 的类型判断功能虽然基础,但掌握其用法和最佳实践,可以显著提升模板的健壮性和灵活性。结合 Java 层处理和模板逻辑控制,能构建出更强大、可维护的视图层逻辑。