在 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
可以是:
string
number
boolean
date
hash
sequence
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 层处理和模板逻辑控制,能构建出更强大、可维护的视图层逻辑。